Other demos:

Tips:

You can find the source code for this demo in js/hang-demo/src/index.html. Yes I know it's confusing when a command automatically opens a browser window.

This demo uses http so it's extra not secure. It works by insecurely fetching the certificate hash and telling WebTransport to trust it. If you're going to run this code in production, you'll need a valid certificate (ex. LetsEncrypt) and use https.


You can instanciate the player via the provided <hang-watch> Web Component. Either modify HTML attributes like <hang-watch paused> or use the Javascript API. The Javascript API is still evolving, so I recommend the Web Component for now.

You can provide your own canvas element and use CSS to modify it. Unfortunately, you can't use the HTML width/height attributes because of how OffscreenCanvas works. For example:

<hang-watch url="http://localhost:4443/" room="demo" path="bbb">
	<!-- Optionally provide a custom canvas element that we can style as needed -->
	<canvas style="max-width: 100%; height: auto; border-radius: 4px;"></canvas>
</hang-watch>

Don't want video? Don't provide a canvas! It won't be downloaded, decoded, or rendered. This includes when the video is paused, minimized, not in the DOM, or scrolled out of view. wowee the bandwidth savings!

Audio may start muted because the browser can require user interaction before autoplaying. You can unmute it by removing the muted property or calling watch.audio.muted.set(false) via the Javascript API. And of course, nothing is downloaded while it's muted.


The Javascript API is far more powerful and you can access properties directly:

const watch = document.getElementById("watch");
watch.audio.muted.set(true);

All of the properties are reactive using a hand-rolled signals library: `@moq/signals`. You could use it... or you can use the provided `react` and `solid` helpers:


import { Watch } from "@moq/hang";
import solid from "@moq/signals/solid";

function Volume(hang: Watch) {
	// Switch to `react` if you're using React, duh.
	const volume = solid(hang.volume);

	// Return a div that displays the volume.
	return <div>
		Volume: {volume()}
	</div>
}
		

Using something more niche? There's also a subscribe() method to trigger a callback on change.


const cleanup = hang.volume.subscribe((volume) => {
	document.getElementById("volume-value").textContent = `${volume * 100}%`;
});

// Cleanup the subscription when no longer needed.
cleanup();
		


The connection and broadcast are automatically reloaded. Try running multiple terminals and kill the broadcast to see what happens.

# Run the relay and web server in another terminal or the background.
just relay &
just web &

just pub bbb
# Kill it with ctrl+C

# Republish the same broadcast, the player will reconnect.
just pub bbb
		

If the Big Bunny is making you sick, you can use other inferior test videos or the publish demo. For example, Try running just pub tos in a new terminal and then watch robots bang. This command uses ffmpeg to produce a fragmented MP4 file piped over stdout and then sent over the network. Yeah it's pretty gross.

If you want to do things more efficiently, you can use the Gstreamer plugin. It's pretty crude and doesn't handle all pipeline events; contributions welcome!