{"id":21015567,"url":"https://github.com/lostbeard/spawndev.blazorjs.simplepeer","last_synced_at":"2025-07-16T08:17:23.078Z","repository":{"id":241594449,"uuid":"807166979","full_name":"LostBeard/SpawnDev.BlazorJS.SimplePeer","owner":"LostBeard","description":"SimplePeer WebRTC video, voice, and data channels for Blazor WebAssembly","archived":false,"fork":false,"pushed_at":"2024-07-31T12:48:55.000Z","size":11260,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-02T23:07:04.749Z","etag":null,"topics":["blazor","blazor-webassembly","browser","dotnet","p2p","webrtc"],"latest_commit_sha":null,"homepage":"https://lostbeard.github.io/SpawnDev.BlazorJS.SimplePeer/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LostBeard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["LostBeard"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"custom":null}},"created_at":"2024-05-28T15:42:28.000Z","updated_at":"2024-07-31T12:48:59.000Z","dependencies_parsed_at":"2024-07-31T16:11:36.228Z","dependency_job_id":null,"html_url":"https://github.com/LostBeard/SpawnDev.BlazorJS.SimplePeer","commit_stats":null,"previous_names":["lostbeard/spawndev.blazorjs.simplepeer"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.SimplePeer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.SimplePeer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.SimplePeer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.SimplePeer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LostBeard","download_url":"https://codeload.github.com/LostBeard/SpawnDev.BlazorJS.SimplePeer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225332382,"owners_count":17457710,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","blazor-webassembly","browser","dotnet","p2p","webrtc"],"created_at":"2024-11-19T10:10:27.131Z","updated_at":"2024-11-19T10:10:27.834Z","avatar_url":"https://github.com/LostBeard.png","language":"C#","funding_links":["https://github.com/sponsors/LostBeard"],"categories":[],"sub_categories":[],"readme":"# SpawnDev.BlazorJS.SimplePeer\n\n[![NuGet](https://img.shields.io/nuget/dt/SpawnDev.BlazorJS.SimplePeer.svg?label=SpawnDev.BlazorJS.SimplePeer)](https://www.nuget.org/packages/SpawnDev.BlazorJS.SimplePeer) \n\n**SpawnDev.BlazorJS.SimplePeer** brings the amazing [simple-peer](https://github.com/feross/simple-peer) library to Blazor WebAssembly.\n\n**SpawnDev.BlazorJS.SimplePeer** uses [SpawnDev.BlazorJS](https://github.com/LostBeard/SpawnDev.BlazorJS) for Javascript interop allowing strongly typed, full usage of the [simple-peer](https://github.com/feross/simple-peer) Javascript library. Voice, video and data channels are all fully supported in Blazor WebAssembly. The **SpawnDev.BlazorJS.SimplePeer** API is a strongly typed version of the API found at the [simple-peer](https://github.com/feross/simple-peer?tab=readme-ov-file#api) repo. \n\n### Demo\n[Simple Demo](https://lostbeard.github.io/SpawnDev.BlazorJS.SimplePeer/)\n\n### Getting started\n\nAdd the Nuget package `SpawnDev.BlazorJS.SimplePeer` to your project using your package manager of choice.\n\nModify the Blazor WASM `Program.cs` to initialize SpawnDev.BlazorJS for Javascript interop.  \nExample Program.cs   \n```cs\nusing Microsoft.AspNetCore.Components.Web;\nusing Microsoft.AspNetCore.Components.WebAssembly.Hosting;\nusing SpawnDev.BlazorJS;\nusing SpawnDev.BlazorJS.SimplePeer;\nusing SpawnDev.BlazorJS.SimplePeer.Demo;\n\nvar builder = WebAssemblyHostBuilder.CreateDefault(args);\nbuilder.RootComponents.Add\u003cApp\u003e(\"#app\");\nbuilder.RootComponents.Add\u003cHeadOutlet\u003e(\"head::after\");\n// Add SpawnDev.BlazorJS interop\nbuilder.Services.AddBlazorJSRuntime();\n// Load the SimplePeer Javascript library. Can be called in a component instead if desired, or loaded using a \u003cscript\u003e tag in the index.html\nawait SimplePeer.Init();\n// Run app using BlazorJSRunAsync extension method\nawait builder.Build().BlazorJSRunAsync();\n```\n\nManualConnectExample.razor  \nBlazor version of: [simple-peer usage example](https://github.com/feross/simple-peer?tab=readme-ov-file#usage)  \n```cs\n@page \"/\"\n@using System.Text;\n@using System.Text.Json;\n@using SpawnDev.BlazorJS;\n@using SpawnDev.BlazorJS.JSObjects;\n@using SpawnDev.BlazorJS.JSObjects.WebRTC;\n@implements IDisposable\n\n\u003cPageTitle\u003eSimplePeer Test\u003c/PageTitle\u003e\n\n\u003ch1\u003eSimplePeer Test\u003c/h1\u003e\n\n\u003cp\u003e\n    An \"offer\" will be generated by the initiator. Paste this into the receiver's form and\n    hit submit. The receiver generates an \"answer\". Paste this into the initiator's form and\n    hit submit. \u003ca href=\"https://github.com/feross/simple-peer?tab=readme-ov-file#usage\"\u003eOriginal Example\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cdiv\u003e\n    Role: @peerRole\u003cbr /\u003e\n    \u003cbutton disabled=\"@(peer != null)\" class=\"btn btn-primary\" @onclick=\"@(()=\u003eInit(true))\"\u003eCreate Initiator\u003c/button\u003e\n    \u003cbutton disabled=\"@(peer != null)\" class=\"btn btn-primary\" @onclick=\"@(()=\u003eInit(false))\"\u003eCreate Receiver\u003c/button\u003e\n\u003c/div\u003e\n\n\u003cdiv\u003e\n    \u003ctextarea style=\"width: 600px; word-wrap: break-word; white-space: normal;\" @bind=@incoming\u003e\u003c/textarea\u003e\n    \u003cbutton disabled=\"@(peer == null)\" @onclick=@Submit\u003esubmit\u003c/button\u003e\n\u003c/div\u003e\n\u003cpre style=\"width: 600px; word-wrap: break-word; white-space: normal;\"\u003e@((MarkupString)outgoing)\u003c/pre\u003e\n\n@code {\n    string peerRole =\u003e peer == null ? \"(select)\" : (peer.Initiator ? \"initiator\" : \"receiver\");\n    SimplePeer? peer = null;\n    string outgoing = \"\";\n    string incoming = \"\";\n\n    void Init(bool initiator)\n    {\n        peer = new SimplePeer(new SimplePeerOptions\n            {\n                Initiator = initiator,\n                Trickle = false,\n            });\n        peer.OnError += SimplePeer_OnError;\n        peer.OnSignal += SimplePeer_OnSignal;\n        peer.OnConnect += SimplePeer_OnConnect;\n        peer.OnClose += SimplePeer_OnClose;\n        peer.OnData += SimplePeer_OnData;\n    }\n\n    void Submit()\n    {\n        peer!.Signal(JSON.Parse(incoming)!);\n    }\n\n    void SimplePeer_OnConnect()\n    {\n        outgoing = \"Connected\u003cbr/\u003e\";\n        StateHasChanged();\n        // send string\n        peer!.Send(\"Hello \" + Guid.NewGuid().ToString());\n        // send byte array (binary data)\n        peer!.Send(new byte[] { 65, 66, 67, 68 });\n    }\n\n    void SimplePeer_OnClose()\n    {\n        outgoing += \"Closed\u003cbr/\u003e\";\n        StateHasChanged();\n    }\n\n    void SimplePeer_OnSignal(JSObject data)\n    {\n        outgoing = JSON.Stringify(data);\n        StateHasChanged();\n    }\n\n    void SimplePeer_OnError(NodeError error)\n    {\n        outgoing = error.Code! + \"\u003cbr/\u003e\";\n        StateHasChanged();\n    }\n    \n    void SimplePeer_OnData(NodeBuffer data)\n    {\n        outgoing += \"Binary: \" + Encoding.UTF8.GetString((byte[])data!) + \"\u003cbr/\u003e\";\n        StateHasChanged();\n    }\n\n    public void Dispose()\n    {\n        if (peer != null)\n        {\n            peer.OnError -= SimplePeer_OnError;\n            peer.OnSignal -= SimplePeer_OnSignal;\n            peer.OnConnect -= SimplePeer_OnConnect;\n            peer.OnClose -= SimplePeer_OnClose;\n            peer.OnData -= SimplePeer_OnData;\n            peer.Destroy();\n            peer.Dispose();\n            peer = null;\n        }\n    }\n}\n```\n\nAn \"offer\" will be generated by the initiator. Paste this into the receiver's form and\nhit submit. The receiver generates an \"answer\". Paste this into the initiator's form and\nhit submit.\n\nNow you have a direct P2P connection between two browsers!\n\n## API\n\n### peer = new SimplePeer([SimplePeerOptions opts])\n\n\nCreate a new WebRTC peer connection.\n\nA \"data channel\" for text/binary communication is always established, because it's cheap and often useful. For video/voice communication, pass the `stream` option.\n\nIf `opts` is specified, then the default options (shown below) will be overridden.\n\n```\n{\n  Initiator: false,\n  ChannelConfig: {},\n  ChannelName: '\u003crandom string\u003e',\n  Config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:global.stun.twilio.com:3478?transport=udp' }] },\n  OfferOptions: {},\n  AnswerOptions: {},\n  Stream: false,\n  Streams: [],\n  Trickle: true,\n  AllowHalfTrickle: false,\n  ObjectMode: false\n}\n```\n\nThe SimplePeerOptions properties do the following:\n\n- `Initiator` - `bool` set to `true` if this is the initiating peer\n- `ChannelConfig` - `RTCDataChannelOptions` custom webrtc data channel configuration (used by [`createDataChannel`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createDataChannel))\n- `ChannelName` - `string` custom webrtc data channel name\n- `Config` - `RTCConfiguration` custom webrtc configuration (used by [`RTCPeerConnection`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) constructor)\n- `OfferOptions` - `RTCOfferOptions` custom offer options (used by [`createOffer`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer) method)\n- `AnswerOptions` - `RTCAnswerOptions` custom answer options (used by [`createAnswer`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createAnswer) method)\n- `Stream` - `MediaStream` if video/voice is desired, pass stream returned from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)\n- `Streams` - `MediaStream[]` an array of MediaStreams returned from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)\n- `Trickle` - `bool` set to `false` to disable [trickle ICE](http://webrtchacks.com/trickle-ice/) and get a single 'signal' event (slower)\n- `ObjectMode` - `bool` set to `true` to create the stream in [Object Mode](https://nodejs.org/api/stream.html#stream_object_mode). In this mode, incoming string data is not automatically converted to `NodeBuffer` objects.\n\n## Methods\n\n### `peer.Signal(object data)`\n\nCall this method whenever the remote peer emits a `peer.OnSignal` event.\n\nThe `data` will encapsulate a webrtc offer, answer, or ice candidate. These messages help\nthe peers to eventually establish a direct connection to each other. The contents of these\nmessages are an implementation detail that can be ignored by the user of this module;\nsimply pass the data from 'signal' events to the remote peer and call `peer.signal(data)`\nto get connected.\n\n### `peer.Send(string/TypedArray/ArrayBuffer/Blob/byte[] data)`\n\nSend text/binary data to the remote peer. `data` can be any of several types: `string`,\n`Buffer` (see [buffer](https://github.com/feross/buffer)), `TypedArray` (`Uint8Array`,\netc.), `ArrayBuffer`, or `Blob` (in browsers that support it).\n\nNote: If this method is called before the `peer.OnConnect` event has fired, then an exception will be thrown. Use `peer.Write(data)` (which is inherited from the node.js [duplex stream](http://nodejs.org/api/stream.html) interface) if you want this data to be buffered instead.\n\n### `peer.AddStream(MediaStream stream)`\n\nAdd a `MediaStream` to the connection.\n\n### `peer.RemoveStream(MediaStream stream)`\n\nRemove a `MediaStream` from the connection.\n\n### `peer.AddTrack(MediaStreamTrack track, MediaStream stream)`\n\nAdd a `MediaStreamTrack` to the connection. Must also pass the `MediaStream` you want to attach it to.\n\n### `peer.RemoveTrack(MediaStreamTrack track, MediaStream stream)`\n\nRemove a `MediaStreamTrack` from the connection. Must also pass the `MediaStream` that it was attached to.\n\n### `peer.ReplaceTrack(MediaStreamTrack oldTrack, MediaStreamTrack newTrack, MediaStream stream)`\n\nReplace a `MediaStreamTrack` with another track. Must also pass the `MediaStream` that the old track was attached to.\n\n### `peer.AddTransceiver(string kind, RTCRtpTransceiverOptions init)`\n\nAdd a `RTCRtpTransceiver` to the connection. Can be used to add transceivers before adding tracks. Automatically called as necessary by `AddTrack`.\n\n### `peer.Destroy([NodeError err])`\n\nDestroy and cleanup this peer connection.\n\nIf the optional `err` parameter is passed, then it will be emitted as an `'error'`\nevent on the stream.\n\n### `SimplePeer.WEBRTC_SUPPORT`\n\nDetect native WebRTC support in the javascript environment.\n\n```cs\nif (SimplePeer.WEBRTC_SUPPORT) {\n  // webrtc support!\n} else {\n  // fallback\n}\n```\n\n## Events\n\n**Note:** Registered event handlers need to be unregistered (`-=` or `RemoveListener`) when they are no longer needed to prevent memory leaks. Lambda event handlers are used here to keep the examples simple.\n\n`SimplePeer` inherits from `EventEmitter`. Event handlers can be added using `JSEventCallback` and `+=/-=` operators or using `EventEmitter.On` and `EventEmitter.RemoveListener` methods.\n\n### `peer.OnSignal += (JSObject data) =\u003e {}`\n\nFired when the peer wants to send signaling data to the remote peer.\n\n**It is the responsibility of the application developer (that's you!) to get this data to\nthe other peer.** This usually entails using a websocket signaling server. This data is an\n`Object`, so  remember to call `JSON.Stringify(data)` to serialize it first. Then, simply\ncall `peer.Signal(data)` on the remote peer.\n\n(Be sure to listen to this event immediately to avoid missing it. For `Initiator = true`\npeers, it fires right away. For `Initiator = false` peers, it fires when the remote\noffer is received.)\n\n### `peer.OnConnect += () =\u003e {}`\n\nFired when the peer connection and data channel are ready to use.\n\n### `peer.OnData += (NodeBuffer data) =\u003e {}`\n\nReceived a message from the remote peer (via the data channel). \n\nFor `ObjectMode = false` peers (default) `data` is a `NodeBuffer`. For `ObjectMode = true`\npeers, `data` can be a `string` or a `NodeBuffer`.\n\n### `peer.OnStream += (MediaStream stream) =\u003e {}`\n\nReceived a remote video stream, which can be displayed in a video tag:\n\n```cs\npeer.OnStream += stream =\u003e {\n    using var document = JS.Get\u003cDocument\u003e(\"document\");\n    using var video = document.QuerySelector\u003cHTMLVideoElement\u003e(\"video\");\n    video.SrcObject = stream;\n    video.Play();\n};\n```\n\n### `peer.OnTrack += (MediaStreamTrack track, MediaStream stream) =\u003e {}`\n\nReceived a remote audio/video track. Streams may contain multiple tracks.\n\n### `peer.OnClose += () =\u003e {}`\n\nCalled when the peer connection has closed.\n\n### `peer.OnError += (NodeError err) =\u003e {}`\n\nFired when a fatal error occurs. Usually, this means bad signaling data was received from the remote peer.\n\n`err` is a `NodeError` object.\n\n## error codes\n\nErrors returned by the `error` event have an `err.Code` property that will indicate the origin of the failure. Constants for these errors can be found in the class `SimplePeer.ErrorCodes`.\n\nPossible error codes:\n- `ERR_WEBRTC_SUPPORT`\n- `ERR_CREATE_OFFER`\n- `ERR_CREATE_ANSWER`\n- `ERR_SET_LOCAL_DESCRIPTION`\n- `ERR_SET_REMOTE_DESCRIPTION`\n- `ERR_ADD_ICE_CANDIDATE`\n- `ERR_ICE_CONNECTION_FAILURE`\n- `ERR_SIGNALING`\n- `ERR_DATA_CHANNEL`\n- `ERR_CONNECTION_FAILURE`\n\n## SpawnDev.BlazorJS.SimplePeer.WebPeer  \n\n[![NuGet](https://img.shields.io/nuget/dt/SpawnDev.BlazorJS.SimplePeer.WebPeer.svg?label=SpawnDev.BlazorJS.SimplePeer.WebPeer)](https://www.nuget.org/packages/SpawnDev.BlazorJS.SimplePeer.WebPeer) \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.simplepeer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.simplepeer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.simplepeer/lists"}