Options & controls reference
Everything is driven by URL parameters plus a few on-screen controls. Add
parameters to a room URL with &, e.g.:
https://cre8stream.com/?room=ROOMID&bitrate=2500&quality=1&label=Jaro
If you omit room, you get the welcome page: pick a nickname (optional), set a
room password (optional), and it creates the room for you — the resulting URL is
instantly shareable. Add autostart to skip the page and auto-generate a room
straight into the address bar, like the old default.
Modes
| Param | Mode | Description |
|---|---|---|
(no room) |
Welcome page | Create a room, with an optional nickname and password. autostart skips it. |
| (none) | Studio | You publish camera/mic and see everyone. The default. |
recv=1 |
Director | You publish nothing, watch everyone, and get the local per-peer controls and the room panel. Good for a producer/monitor seat — the panel shows a ready-made Director link to share. |
view=PEERID |
OBS view | A clean, chrome-free page showing exactly one peer — for an OBS Browser Source. No controls, cursor hidden. |
board |
OBS whiteboard | A clean, chrome-free page showing just the shared 🎨 whiteboard — for an OBS Browser Source (the room panel offers a ready-made link). Blank until someone opens the board, then follows it live; builds no peer connections (zero decode cost). Ignored when view= is present. |
First-time guests opening a ?room= invite see a one-field nickname prompt
(optional — joining works with it empty). A label= param, a nickname remembered
from a previous visit, or autostart skips the prompt. Directors (recv=1) and
OBS view= pages never see it.
Password-protecting a room
| Param | Values | Effect |
|---|---|---|
password=SECRET |
text | Gates the room with a shared secret. room=abc&password=A and room=abc&password=B are entirely different rooms — only people with the same room and password ever meet. Same model as vdo.ninja. |
The password is hashed into the signaling room name in your browser (SHA-256), so
the plaintext never reaches the server — only the derived room name travels the
wire. Invite and OBS links generated in-app automatically include &password= so
they stay click-to-join.
Honest caveat: because the password rides in the link (exactly like vdo.ninja), it
can land in browser history and in your web server's access log. You self-host, so
that log is yours — but for stricter use, share the link without &password= and
have guests append the secret themselves.
Publishing options
| Param | Values | Effect |
|---|---|---|
label=NAME / name=NAME |
text | Your display name — shown on your tile on every peer's screen and in the room panel. Also settable on the welcome/join screens; remembered by your browser for next time. |
push=ID |
1–32 of A–Z a–z 0–9 _ - | Claim a specific peer id instead of a random one — a personal, one-guest invite link. Accepted exactly or ignored. One id = one seat: a second person opening the same link takes over and the first is disconnected. Make these from the room panel's Personal invites. |
bitrate=KBPS |
200–20000 | Caps your outbound video bitrate. Values outside the range are clamped. Also adjustable live via the vid ▾ select on the You row of the room panel. |
audiobitrate=KBPS |
6–512 | Caps your outbound audio (Opus) bitrate. Values outside the range are clamped. Also adjustable live via the aud ▾ select on the You row of the room panel. Best-effort — the browser maps it onto Opus's target bitrate. |
codec=... |
h264 vp8 vp9 av1 |
Preferred video codec. Defaults to h264. Preference only — it reorders, never removes, so peers without it still connect. |
quality=N |
0 1 2 |
Capture preset: 0 = 1080p, 1 = 720p (default), 2 = 360p. |
width= height= |
pixels | Capture size (overrides quality). Treated as "ideal", so the camera never errors if it can't match exactly. |
fps= |
number | Capture frame rate ("ideal"). |
ec= |
0 1 |
Browser echo cancellation on your mic. Default on (that's every browser's own default — today's behaviour); set ec=0 to disable, e.g. for music. ⚠️ Without headphones, disabling it makes everyone else hear themselves echo through your speakers. Applied to the raw mic capture — zero added latency, best-effort ("ideal"), never blocks joining. Also a ⚙️ checkbox, remembered (a link param overrides the remembered choice for that visit). ec/ns/agc change your outbound mic — what others hear of you; you can't hear your own processing, so verify a change with a second participant (or a local recording). |
ns= |
0 1 |
Browser noise suppression on your mic. Default on; set ns=0 to let room sound through unprocessed (music, instruments). Same mechanics as ec=. |
agc= |
0 1 |
Browser auto mic gain (level riding). Default off — auto gain fights the input level you stage in your own hardware / OS / OBS, so that's left to you; set agc=1 to let the browser ride your level instead. Same mechanics as ec=; silently ignored (and the ⚙️ checkbox hides itself) on browsers without AGC support. |
noisegate |
flag, dB −90…−20, or 0 = off |
Noise gate on your outgoing mic: fully mutes it between words, so keyboard/fan/room noise never reaches the room. Optional value sets the open threshold in dBFS (default -50; lower = more sensitive); noisegate=0 explicitly turns it off, overriding a remembered ⚙️ opt-in. Also a ⚙️ checkbox ("mic noise gate"), remembered. Honesty note: unlike recording/stats/glow, this puts real processing on your outbound audio path — expect ~10–30 ms added to your own voice (Web Audio processing quanta + the leveler's fixed ~6 ms look-ahead). Your video and everyone else's audio are unaffected; that's within typical network jitter, but it's opt-in for a reason. On browsers without AudioWorklet the gate is skipped (the leveler still applies) and its checkbox hides itself. |
compressor |
flag, or 0 = off |
Leveler (compressor) on your outgoing mic: evens out your volume — quiet asides come up, sudden peaks come down (gentle 3:1 voice preset). compressor=0 explicitly turns it off, overriding a remembered ⚙️ opt-in. Also a ⚙️ checkbox ("mic leveler"), remembered. Same ~10–30 ms outbound-latency note as noisegate; combine both (&noisegate&compressor) for the full clean-up chain. |
mute / muted |
flag | Join with your mic muted (toggle it back on with 🎙️). |
layout= |
auto row col |
Tile layout direction, for the main grid and the ⧉ PiP window. auto (default) packs tiles into whatever column count fills the window best; row puts everyone in one horizontal row and col in one vertical column, so a new joiner extends the line instead of re-packing the grid. Purely local — it changes only what you see, nothing is sent and nobody else's view moves. Also a ⚙️ picker ("tile layout"), remembered. When a forced row/column can no longer fit at a usable tile size the direction is kept and the grid scrolls, rather than silently reverting to the packed layout you didn't ask for. An unrecognised value falls back to auto. |
hud= |
0 1 |
Hide the HUD — the header, participant panel, chat panel and each tile's name label, per-peer controls and &stats readout — leaving just video. Default on (hud=0 to start hidden). Also the ▤ HUD button; a ghosted ▤ in the top-right corner brings it all back, and the choice is remembered. Works inside the ⧉ PiP window too. Deliberately not hidden: the active-speaker glow, a raised hand shown on a tile, your own mute button, the 🎨 whiteboard's toolbar, and the PiP window's own mute/chat/grow buttons (hiding the mute would leave you no way to mute from that window). Local view only — nothing changes for anyone else, and nothing about what OBS receives. |
novideo |
flag | Join audio-only (no camera captured). |
noaudio |
flag | Join video-only (no mic captured). |
autostart |
flag | Skip the welcome/join screens: auto-creates a room when room is absent, joins instantly when it's present (an unnamed join shows as peer <id>). For kiosk/companion links. |
stats |
flag | Overlay a live diagnostics readout on each tile — inbound kbps + RTT on remote tiles, outbound kbps on your own. Read straight from WebRTC's own counters (no messages sent, media path untouched). Handy for verifying a bitrate cap: received kbps sits a little under a set bitrate=/force ceiling by design (the cap is a max, not a target; counters are payload-only; congestion control trims further). Off unless present. |
debug |
flag | Surface otherwise-swallowed runtime errors on the status line (plus a capability banner in the console at boot). Use it to triage a browser that connects to nothing — especially a locked-down/enterprise Firefox. Off unless present. |
A flag is "on" simply by being present: ?muted and ?muted=1 both mute you.
Examples
# 720p, mic muted on join, capped at 2.5 Mbps
/?room=abc&quality=1&muted&bitrate=2500
# Audio-only guest (no camera)
/?room=abc&novideo&label=Podcast%20Guest
# Force VP9 instead of the H.264 default
/?room=abc&codec=vp9
# A clean OBS source for one specific peer
/?room=abc&view=PEER123
On-screen controls
Your controls (header, when publishing)
| Button | Action |
|---|---|
| 🎙️ | Mute / unmute your mic. Keyboard shortcut: M (except while typing in a text box). |
| 🎤 Enable mic | Appears only if you joined without a mic (permission denied or the device was busy — see the Android note below). Grants and adds your microphone live, without a reload. Disappears once the mic is added. |
| 📷 | Turn your camera off / on. |
| 🖥️ | Share your screen (replaces your camera feed; stop to return to camera). |
| ⏺ Record | Record your own camera/mic/screen to this device — nothing is uploaded. It taps the tracks you already send (no latency; CPU only while recording). Click again to stop and download. See the note below. |
| ⚙️ | Switch your camera / microphone live (publishers), and pick a speaker / sound output for the room's audio where the browser supports it (Chromium; local playback only — it changes what you hear through, never what anyone sends). Choices are remembered for your next visit. The speaker row hides itself when there's no real choice (browsers expose outputs only after mic permission; Firefox not at all); directors always get ⚙️ — it holds the device-independent tile layout picker, and the speaker row inside it hides itself when there is no real output choice. An unplugged output falls back to the system default. Also holds a tile layout picker (&layout= above — auto / one row / one column, remembered) and a hide my own tile in PiP toggle that drops your self-view from the ⧉ PiP window so you can monitor just the others. Publishers with a mic additionally get three mic processing checkboxes — echo cancellation, noise suppression, auto mic gain (&ec=/&ns=/&agc= above) — applied live, remembered, each hidden where unsupported (a browser that can't change these live gets a one-shot mic re-capture instead — which, mid-recording, also starts a new -partN part unless clean-up is on) — plus the two mic clean-up toggles (noise gate / leveler, &noisegate/&compressor above): opt-in outbound processing (~10–30 ms on your own voice), remembered, hidden where unsupported; toggling one mid-recording starts a new -partN part, and while clean-up is on a mic switch no longer splits the recording at all. |
| ⧉ PiP all | Pop the whole grid into a floating always-on-top window (Chrome/Edge 116+, Firefox 151+; Safari gets the per-tile ⧉ instead). The window opens sized for a near-square packing (4 tiles → 2×2, not one long strip). After that it never resizes itself — a window you dragged into a corner of your screen stays where you put it — it just re-picks the column count that makes the tiles as large as the window allows in both directions when someone joins or leaves and when you drag-resize it. To make it big in one click, press ⤢ in the bottom-right corner of the PiP window: it asks for 80% of your screen and never shrinks a window you already made larger. Browsers cap how large a page may make its own picture-in-picture window, so you may get less than you asked for. Both this and the per-tile ⧉ float above other apps at the OS level, so you can keep an eye on the room while working elsewhere — the active-speaker glow shows there too. Per-peer mute/volume, your self 🎙️, and the M key all work inside the PiP window; the first click there also unlocks audio. With "hide my own tile in PiP" on, a standalone 🎙️ Mute button appears at the bottom of the PiP window so you can still mute yourself while your tile is hidden. |
| 🎨 Board | Open a shared whiteboard — an extra tile everyone sees and everyone can draw on (directors too). Strokes travel over the signaling channel like 💬 messages: nothing touches the media path, and connection setup is unaffected. Toolbar: four pen colors, pen-width cycle, ↩ undo your last stroke, 🗑 clear all (for everyone, confirmed), ⛶ enlarge, ✕ close for everyone (drawing discarded). Late joiners get the current drawing automatically; the board dies with the room and never shows on the clean view= OBS feed. To put the drawing on stream, add the panel's ready-made &board link as its own OBS Browser Source. In bigger rooms remote strokes update a few times a second (batched) — your own drawing is always instant. |
\o/ Raise | Raise your hand. A small \o/ badge appears on your tile for everyone in the room, carrying the position you're in — \o/ 1 for whoever raised first, \o/ 2 next, and so on — and the panel lists the queue in order. Press again to lower it. A director (recv=1) publishes no video and so has no tile — their hand shows in the panel only, which means it is not visible to anyone running with the HUD hidden. Only you can lower your hand: there is no host override, on purpose (that would need a signed channel the relay deliberately doesn't have). Costs one tiny message per person, never touches video or audio, and never appears on an OBS view= feed. Honest limit on the numbers: members' clocks are never synchronised, so each raise is announced as "I raised this long ago" and every browser re-bases that onto its own clock. The order is accurate to network latency, not to the millisecond — two hands raised within a fraction of a second of each other can be numbered differently by different people; anything with real space between them is consistent for everyone. |
| ▤ HUD | Hide the header, panel and per-tile overlays for a clean, video-only view (&hud= above). A ghosted ▤ in the top-right corner restores it, and the choice is remembered. |
| 🔗 | Copy the room invite link. |
| 🎥 cre8stream | The header title links back to the home page. In a live room it asks you to confirm before leaving. |
Recording (⏺): captures only your own camera/mic/screen to a file on your
device — no upload, no effect on what peers get, no added latency (CPU only while it runs). It's held in
memory until you stop, so a tab crash loses it (the page warns before an accidental close while recording);
Chrome may ask once to allow multiple downloads. It records exactly what peers see (muted mic → silence,
camera off → black). Switching camera/mic, starting/stopping a screen share, adding a mic, toggling
mic clean-up (noise gate / leveler), or toggling a mic-processing checkbox on a browser that
can't apply it live starts a new -partN file — a browser can't record across a changing
source without freezing. (Exception: while mic clean-up is on, switching microphones does
not split the recording — peers and the recorder keep the same processed track.)
Active-speaker highlight: whoever is talking gets a green glow around their tile (your own tile too). It's driven purely by audio levels the browser already measures — nothing extra is sent and it adds no latency to the connection.
Hearing each other: browsers block autoplay-with-sound until you interact with the page, so a freshly-created room can look connected but silent. The first tap/click/keypress anywhere unlocks audio for every participant automatically — a muted peer tile shows 🔇, and clicking it (or anything) starts the sound. No browser "show controls" trick needed.
Phones / Android: if the mic was denied or held by another app at join, you'll come in video-only with a 🎤 Enable mic button — grant the mic in the browser (and in the phone's app settings if needed), then tap it. If you switch away to another app the tab is suspended by the OS (a limit no web app can override); switching back re-plays video, re-gathers a dropped connection, and re-captures the camera automatically, so you shouldn't need to reload.
Per-peer controls (each remote tile, studio/director view)
These affect only what you see and hear — they send nothing to the other person
and never appear on a clean OBS view= page.
| Control | Action |
|---|---|
| 🔊 / 🔇 | Mute/unmute that peer for you. |
| Volume slider | Set that peer's playback volume, 0–200%. Above 100% boosts it via a local audio tap (with a limiter so a loud voice won't clip) — local playback only, nothing is sent to anyone. Double-click to reset to 100%. |
| ⧉ | Picture-in-picture that peer into a native, always-on-top floating window (where the browser supports it). |
| OBS | Copy that peer's chrome-free Browser Source link. |
Your own tile (framing aids)
Controls in the top-right of your own preview. The framing aids are display-only overlays — they are never encoded into your video, so they can't show up in OBS or on anyone else's screen.
| Control | Action |
|---|---|
| 🎙️ | Mute / unmute your mic (mirrors the header button and the M key). |
| ▦ | Toggle a rule-of-thirds grid over your preview to help you frame the shot. |
| ◧ | Toggle a false-color exposure check: clipped highlights turn pink, crushed shadows green, mid-tones grayscale — dial in exposure at a glance. |
The room panel (bottom bar)
Studio and director pages show a panel of copyable links plus per-participant tools:
- Invite — the room link (already carrying
&password=if the room has one). - Director view — the invite plus
recv=1, for a producer/monitor seat. - Personal invites — + new personal link mints a per-guest link with a fixed
&push=<id>plus a matching OBS link, both valid before the guest joins. Each tab also keeps a stable id, so a guest's refresh (F5) doesn't drop their seat and yourview=Browser Source keeps working. Ids aren't secrets — anyone with the link can take that seat (same trust model as the room link). - OBS Browser Source(s) — one link per publisher, labelled by nickname. Directors and OBS views aren't listed (they have no feed to pull).
- ping (next to each participant) — one-shot round-trip time of your direct P2P link to them, read from WebRTC stats. No message is sent and the relay is not involved; green < 80 ms, yellow < 200 ms, red above.
- vid ▾ / aud ▾ on the You row — your own outbound
bitrate caps (video auto / 6.0 / 2.5 / 1.0 / 0.5 Mbps; audio auto / 512 / 256 / 192 / 128 /
96 / 64 / 32 kbps), in the same place, style, and descending order as the per-peer force
selects below — they rest on the same vid ▾ / aud ▾ label while no cap is set.
Seeded from
bitrate=/audiobitrate=(a non-preset value shows as its own entry) and updated live when a room member force-caps you. - vid ▾ / aud ▾ (next to each participant) — cap that participant's outbound video (auto / 6.0 / 2.5 / 1.0 / 0.5 Mbps) or audio (auto / 512 / 256 / 192 / 128 / 96 / 64 / 32 kbps — the same choices as your own aud ▾) bitrate. Use it when someone's weak uplink is degrading the room. The cap applies to everything they send, to every participant — one encoder feeds the whole mesh, so this isn't a "just for me" control. It's a cooperative request: their client applies the cap, mirrors it live on their own You row, and shows who set it — nothing is silent, and a modified client could decline. The request rides the normal signaling channel; the server is unchanged and never inspects it. A force is remembered in this tab, per room, and re-applied automatically when that participant reconnects — it survives a refresh on either side. Choose auto to lift the cap.
- 💬 (next to each participant) — send that person a short direct message (up to 500 characters). It appears as a dismissable toast in their bottom-right corner only — not on anyone else's screen or the OBS feed. Publishers only, and fire-and-forget (same cooperative payload as vid ▾ / aud ▾; no delivery receipt).
Want to mute a guest for everyone? That needs a host-enforced control channel the dumb relay deliberately doesn't have — see the architecture doc. For now these are local-only, which is the honest, no-surprises behavior.
The BLAME button 🔥
Click 🔥 BLAME!!!11one to stress-test every connection at once and crown the participant whose uplink is most likely dragging the room down. It's fully passive — it only reads the quality counters (RTT, jitter, inbound loss) WebRTC already keeps, sends no probe traffic, and adds no latency.
- Only you see the chart. The person who clicked gets the results chart and the
loser gets a 10-second "LOOSER!!!" badge on their tile — on your screen only.
Everyone else just sees a one-line status, and the clean OBS
view=feed shows nothing. - Live monitoring (⟳ live). Toggle
⟳ liveon the card to re-measure every 8 seconds and watch the chart update in place — silent for everyone else (no badge, no status). - Cooperative & honest. It rides the same signaling payload as vid ▾ / aud ▾; the badge is cosmetic and self-applied. A party trick, not an audit.
OBS tips
- Use a separate Browser Source per guest (
…/?room=ROOM&view=PEERID). Each is independent and positionable; composite them with OBS scenes. - The view page prefers H.264 so CEF (the browser inside OBS) decodes it reliably — this matters on Linux where CEF may lack hardware VP9 decode.
- Guest audio: CEF blocks autoplay-with-audio. In the Browser Source properties, enable "Control audio via OBS" so the guest's audio routes into your mix.
- The cursor is hidden on the view page, so no stray pointer lands in your program.
- Every browser tab, director tab and OBS Browser Source occupies one
peer slot in the room (
MAX_PEERS_PER_ROOM, default 8). A 3-guest show with a source per guest plus a director already sits at 7–8 slots — if you hitroom-full, close a spare tab/source and use the ↻ retry button, or ask your operator to raise the cap. A refresh takes over its own seat (it never adds one), and an abruptly-closed context frees its seat within about two minutes. - Set the Browser Source resolution to match your scene (e.g. 1920×1080) and let
the guest's
quality/bitratecontrol the actual stream.