Custom sources on the web Web
This guide is exclusively for Web (React) applications. For React Native, see Custom sources in React Native.
This guide demonstrates how to stream non-standard video or audio to other peers in your web app. The utilities in this section allow you to integrate Fishjam with powerful browser APIs such as WebGL and WebGPU, or higher level libraries, which leverage these APIs, such as Three.js, Smelter or PixiJS. For a complete WebGPU render-and-publish pipeline, see WebGPU effects.
Creating a custom source - useCustomSource()
To create a custom source, you only need to do two things:
- Call the
useCustomSourcehook. - Call the
setStreamcallback provided by theuseCustomSourcehook with a MediaStream object.
Usage Example
importReact , {useEffect } from "react"; import {useCustomSource } from "@fishjam-cloud/react-client"; conststream :MediaStream | null = null; // Replace with your MediaStream object const {setStream } =useCustomSource ("my-custom-source");useEffect (() => {setStream (stream ); }, [stream ,setStream ]);
Using a created custom source
Once you have called setStream for a given source ID (in the above example, "my-custom-source"), any subsequent calls to useCustomSource with the same ID will return the same state.
This enables multiple components to control and read a shared custom source.
Deleting a custom source
If you wish to remove a custom source, then you should call the setStream callback with null as its argument.
Usage Example
const {setStream } =useCustomSource ("my-custom-source"); // ... awaitsetStream (null);
How to get a MediaStream object?
Depending on your use-case, the way you can obtain a MediaStream will vary. Below are some common examples of how to obtain one from an existing video or audio source.
Canvas
If you have a <canvas> HTML element, then you will need to call the captureStream method of the given canvas.
This is particularly useful if you are using Three.js or PixiJS to render complex animations and more.
Usage Example
importReact , {useCallback ,useState } from "react"; export functionCanvasExample () { const [canvasStream ,setCanvasStream ] =useState <MediaStream >(); conststreamFromCanvas =useCallback ((canvas :HTMLCanvasElement | null) => { if (!canvas ) return;setCanvasStream (canvas .captureStream ()); }, []); // use canvasStream as desired return <canvas ref ={streamFromCanvas } />; }
Smelter
If you are using Smelter, then all you need to do is register an output via registerOutput.
Usage Example
If you want to see a full example React app which uses Fishjam with Smelter, then you can check out our full example on GitHub.
importReact from "react"; importSmelter , {setWasmBundleUrl } from "@swmansion/smelter-web-wasm"; import {View } from "@swmansion/smelter";setWasmBundleUrl ("/assets/smelter.wasm"); async functionrun () { constsmelter = newSmelter ({framerate : 30 }); const {stream } = awaitsmelter .registerOutput ("example-output", <View />, {type : "stream",video : {resolution : {width : 1920,height : 1080 }, }, }); awaitsmelter .init (); }
Video/Audio HTML tag
If you have a <video> or <audio> element, then you need to call their captureStream method, analogously as you would with a canvas.
Usage Example
- Video
- Audio
importReact , {useCallback ,useState } from "react"; consthasCaptureStream = (video :HTMLVideoElement ):video isHTMLVideoElement & {captureStream : () =>MediaStream } => "captureStream" invideo && typeofvideo .captureStream === "function"; export functionCanvasExample () { const [stream ,setStream ] =useState <MediaStream >(); conststreamFromVideo =useCallback ((video :HTMLVideoElement | null) => { if (!video ) return; if(!hasCaptureStream (video )) {console .warn ("captureStream is not supported on this browser"); return; }setStream (video .captureStream ()); }, []); // use stream as desired return <video ref ={streamFromVideo } /* ... */ />; }
importReact , {useCallback ,useState } from "react"; export functionCanvasExample () { const [stream ,setStream ] =useState <MediaStream >(); conststreamFromAudio =useCallback ((audio :HTMLAudioElement | null) => { if (!audio ) return;setStream ((audio asHTMLAudioElement & {captureStream ():MediaStream }).captureStream ()); }, []); // use stream as desired return <audio ref ={streamFromAudio } /* ... */ />; }
Related guides
- Custom sources overview: how custom tracks reach other peers
- Custom sources in React Native
- How custom sources work