18 KiB
Streaming Vash matches
For a first test on Windows with Ubuntu WSL, follow the WSL setup guide.
Vash can put its matches on stream by itself. There are three ways to use it, from no PC at all to a full tournament broadcast.
| Setup | What viewers see | Needs |
|---|---|---|
| Vash TV (headless container) | Vash's own data: scoreboard, mappool with picks and bans, live scores, predictions, chat, ticker | A small cloud VM or any Docker host |
| Overlay in OBS | Your OBS scenes with the Vash overlay on top | OBS |
| Full tourney broadcast | Real osu!tourney gameplay under the overlay, live scores, and the same multistream and recording | Dedicated Docker host with Wine, osu!, and tosu, or a Windows PC with OBS |
Vash match runtime ─▶ director ─▶ channel websocket ─▶ tourney agent
▲ │ room selection
│ live scores ▼
└────────── tosu ◀────────────── osu!tourney under Wine
│ gameplay screen
Vash overlay page ──green key──▶ ffmpeg compositor ◀┘
│ one encoded stream
├──▶ Twitch / YouTube / other RTMP
└──▶ Vash live video and recording
1. Create a channel
Referees and admins open Broadcast in the sidebar and create a channel. A channel shows one of these:
- Whatever is most worth watching. The director picks among all live matches: tournament matches first, then close series near match point, tiebreakers, and busy matches. It never cuts away during a map and stays on a result for 30 seconds.
- A tournament's matches, by the same rules.
- One match.
Pick a starting layout; each shows a live preview:
| Layout | Use it when |
|---|---|
| Over osu! gameplay | Capture osu!tourney in the autonomous container or OBS. The gameplay scene leaves a see-through window for it. |
| Vash only | The automatic stream (Vash TV), or any stream without an osu! capture. Everything comes from Vash's data. |
| Scoreboard only | You have your own OBS scenes and only want the score on top. |
| Phone (portrait) | TikTok, Instagram or YouTube Shorts. Makes a 9:16 channel. |
The agent token shown after creating the channel is for the stream container and vash-streamer. It is shown once; Agent token makes a new one.
Layout editor
Edit layout opens the editor. Each scene can be arranged on its own: Starting soon, Lobby, Pick & ban, Gameplay and Results. The overlay switches scenes as the match moves on.
- Drag a component to move it and drag its corner to resize. Movement snaps to a 10 px grid; hold Alt to move freely.
- Arrow keys nudge the selected component (Shift for 10 px). Delete removes it and Ctrl+D duplicates it.
- Components: scoreboard, live score, now playing, mappool, turn and timer, map history, lobby chat, crowd predictions, players, winner, gameplay window, live ticker of other matches, schedule, text, image, and clock.
- Backgrounds: each scene can be see-through, the theme background, or an image.
- Demo match: when nothing is live, the editor shows a made-up match so every component has something to draw.
Saving updates every open overlay immediately, including the one in OBS.
The overlay link
https://<site>/broadcast/<channel>/overlay is a 1920×1080 page with a transparent background. It needs no login and shows only public match data.
?bg=solidpaints the see-through scenes too, for streams with nothing underneath.?scene=gameplaypins one scene, if you prefer one browser source per OBS scene.
2. Overlay in OBS
Add a Browser source with the overlay link, 1920×1080, on top of your scene. That is all. To put gameplay under it, capture anything below the browser source; the gameplay scene of the Tourney client preset leaves that area see-through.
3. Full tourney broadcast (Windows)
Live scores for real lobbies only come from the osu! tourney client. Bancho's IRC only reports results once a map ends. So the streaming PC runs:
- osu! tourney client. Put a
tournament.cfgnext toosu!.exe(see the osu! wiki) and start it with a spare account. - tosu (tosuapp/tosu). It reads the tourney client's memory. Its defaults work.
- OBS 28 or newer with Tools → WebSocket Server Settings enabled.
- vash-streamer.exe, built with
bun run streamer:build(output indist/). It is one file with no install.
# Once: create a "Vash" scene with the tourney client capture and the overlay on top.
.\dist\vash-streamer.exe --server https://esports.vash.software --channel <id> --token <token> --obs-password <pw> --setup-obs
# Every stream:
.\dist\vash-streamer.exe --server https://esports.vash.software --channel <id> --token <token> --obs-password <pw> --obs-scenes '{"idle":"Starting Soon","gameplay":"Vash"}'
While it runs:
- Live scores. Live scores from tosu reach Vash four times a second, matched to players by osu! id. They drive the overlay's live score bar and the match page on the site.
- OBS scenes. With
--obs-scenes, OBS switches scenes as the match does. The scene names areidle,lobby,pickban,gameplayandresults. - On Windows, select the named room in osu!tourney's bottom control panel. The container setup below performs this selection automatically under Wine/X11.
The lazer tournament client can run alongside for its own screens, since it reads the stable client's IPC. Vash does not drive it; the Vash overlay covers the same ground with live data.
4. Go live on Twitch, YouTube and more
Open a channel's Stream page:
- Add destinations. Twitch, YouTube, Facebook, X, Instagram, TikTok, or any RTMP server; several of one kind is fine (two YouTube channels, say). Each platform shows where to find its stream key. Keys are stored encrypted and only sent to this channel's stream container.
- Pick the shape and quality. Landscape (16:9) for Twitch, YouTube, Facebook and X; portrait (9:16) for TikTok and Instagram. To stream both at once, make a second, portrait channel: both follow the same matches.
- Start the stream container on your PC or Docker host, then press Go live. Go live changes the channel's state; it does not launch Docker. A connected container goes on air, reports each destination's state on the page, and reconnects any destination that drops. End stream tells it to stop streaming.
Recordings. Every broadcast is also cut into 2-second HLS segments and uploaded to storage (R2). That is the live stream on the site (the Live page and the channel's Watch page), and afterwards the recording, with chapters that jump to each match. Match pages link to the broadcast that showed them. Delete old recordings from the Stream page.
For playback in Chrome and Firefox, the bucket needs a CORS rule (Cloudflare dashboard → R2 → bucket → Settings → CORS policy):
[
{
"AllowedOrigins": [
"https://esports.vash.software",
"https://esports-dev.vash.software",
"https://esports-staging.vash.software"
],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]
VODs on the platforms. YouTube keeps every stream as a video by itself. On Twitch, turn on Store past broadcasts (Settings → Stream); Twitch keeps them 7 days, or 60 for Partners, Turbo and Prime, so the copy on Vash's storage is the one that lasts. Facebook and X keep the post; Instagram asks to share it when the stream ends.
5. The stream container (Vash TV)
The streamer/tv image renders a channel in Chromium on a virtual screen, encodes it once, and copies the stream to every destination and to the recording. It follows the channel's Go live switch, so the container can idle between matches. To use your own PC for now, build and run it there:
docker build -t vash-tv streamer/tv
docker run -d --restart unless-stopped --shm-size=1g --name vash-tv \
-e VASH_SERVER=https://esports.vash.software \
-e VASH_CHANNEL=<channel id> \
-e VASH_TOKEN=<agent token> \
vash-tv
For gameplay from a Windows PC, the simplest setup is osu!tourney, tosu, OBS, and the Vash streamer agent from sections 2–3. Stream directly from OBS to your platform. In that setup, the site's Go live switch and on-air status do not control OBS. If you also want the site's watch page, recording, and destinations, run Vash TV in relay mode on the same PC and send OBS to its local RTMP input:
docker run -d --restart unless-stopped --name vash-relay -p 127.0.0.1:1935:1935 \
-e VASH_SERVER=https://esports.vash.software -e VASH_CHANNEL=<channel id> \
-e VASH_TOKEN=<agent token> -e INGEST_KEY=<private ingest key> vash-tv
In OBS, set a Custom stream server to rtmp://127.0.0.1:1935/live and use the same private ingest key. Start the container, press Go live on the Stream page, then start streaming in OBS. Stop OBS and press End stream when finished.
| Variable | |
|---|---|
VASH_SERVER, VASH_CHANNEL, VASH_TOKEN |
The site, the channel, and its agent token. Everything else comes from the channel's settings. |
INGEST_KEY |
Relay mode: instead of rendering, listen for OBS on rtmp://<host>:1935/live with this key (INGEST_PORT to change the port). What OBS sends is recorded and sent to every destination unchanged. Use it for the full tourney broadcast so it is recorded and multistreamed too: in OBS, Settings → Stream → Custom, server rtmp://<host>:1935/live, key INGEST_KEY. |
OVERLAY_ORIGIN |
Load the overlay from another address, e.g. http://127.0.0.1:3102 when the container runs next to a dev site behind a login. |
MUSIC_URL |
An audio file or stream to loop under the broadcast (silence otherwise). |
OVERLAY_URL + RTMP_URL |
Without Vash managing it: one overlay to one RTMP destination (WIDTH, HEIGHT, FPS, VIDEO_BITRATE apply). |
Measured on the Vash VM, 720p30 uses about half a CPU core and 510 MB of memory; 1080p and busy scenes use more. Give it 2 vCPUs and 2 GB, and keep it off the Vash server. Upload bandwidth is the bitrate once per destination, plus once for the recording.
Google Cloud (e2-medium, about $25 a month). Container-Optimized OS comes with Docker:
gcloud compute instances create vash-tv --machine-type=e2-medium --zone=europe-west4-a \
--image-family=cos-stable --image-project=cos-cloud \
--metadata=startup-script='docker run -d --restart unless-stopped --shm-size=1g --name vash-tv \
-e VASH_SERVER=https://esports.vash.software -e VASH_CHANNEL=<id> -e VASH_TOKEN=<token> <registry>/vash-tv'
AWS: an EC2 t3.small with Docker and the docker run above (about $15 a month), or an ECS Fargate task with 2 vCPU and 4 GB (about $70 a month running full time). For relay mode, open TCP 1935 to your streaming PC only.
Stop the instance when nothing is scheduled; the channel's recordings stay in storage.
6. Autonomous osu!tourney container
The streamer/tourney image adds a separate virtual screen for the stable osu!tourney client, starts it under Wine, reads its scores with tosu, and keys the Vash overlay over the gameplay in ffmpeg. The same encoder sends to all enabled destinations and records to the site. The director selects the room automatically from osu!tourney's control panel. It retries while the room is still opening.
osu!tourney needs a dedicated osu! stable installation and an osu! account with an active supporter tag. Use an account you are allowed to operate, and sign in once with Remember Username and Remember Password. Set OSU_TOURNEY_ACCOUNT to that username in the Vash server environment so each new lobby grants it referee access. The container does not store an osu! password in Vash. All new Vash lobbies use the VASH: (Red) vs (Blue) naming required by this single osu!tourney installation. The agent restarts osu!tourney when a match needs a different TeamSize; it never edits tournament.cfg while the client is running.
Build the image from the repository root:
docker build -f streamer/tourney/Dockerfile -t vash-tourney streamer
Prepare a persistent directory owned by uid 1000. Place a fresh, separate osu! stable installation at /path/to/vash-tourney-data/osu/osu!.exe and the executable Linux build of tosu at /path/to/vash-tourney-data/tosu/tosu. The tosu project documents Wine support; this setup still needs a real lobby test on your host. A GPU host is recommended until performance is measured for your team size and resolution.
First sign in to osu! in normal mode. For a fresh Wine prefix, OSU_DOTNET=1 installs .NET Framework 4.8 with winetricks before launching osu!; the installer may take several minutes and download Microsoft files. If the mounted prefix already has a working .NET installation, omit that variable. Use a strong temporary VNC password and keep the web port bound to localhost, then reach it through SSH forwarding if the Docker host is remote:
docker run --rm --shm-size=1g --cap-add SYS_PTRACE \
-v /path/to/vash-tourney-data:/data -p 127.0.0.1:6080:6080 \
-e OSU_SETUP=1 -e OSU_DOTNET=1 -e VNC_PASSWORD='<private password>' vash-tourney
# Open http://127.0.0.1:6080/vnc.html, sign in to osu!, then stop the container.
Create a broadcast channel with the Over osu! gameplay layout, add Twitch, YouTube, or other destinations, and copy its one-time agent token. Then run:
docker run -d --restart unless-stopped --shm-size=1g --cap-add SYS_PTRACE \
--name vash-tourney -v /path/to/vash-tourney-data:/data \
-p 127.0.0.1:6080:6080 \
-e VNC_PASSWORD='<private password>' \
-e VASH_SERVER=https://esports.vash.software \
-e VASH_CHANNEL='<channel id>' -e VASH_TOKEN='<agent token>' \
vash-tourney
The Go live switch controls ffmpeg and destinations; the tourney client stays ready to follow matches. Watch docker logs -f vash-tourney for room selection and tosu connection. The VNC screen shows the osu!tourney control panel if it needs attention. Live scores appear when tosu reads the spectated clients. The container captures the top 1280×720 area of osu!tourney and the bottom control panel only for room selection. Set GAMEPLAY_WIDTH and GAMEPLAY_HEIGHT to change that capture size, and use the matching channel quality. The compositor also supports portrait outputs, with the 16:9 gameplay letterboxed under the portrait overlay.
This uses the osu!tourney room naming and slot rules described in the official multiplayer guide and the official setup guide. The IPC file is only needed for the separate osu!(lazer) tournament overlay; this container draws the Vash overlay itself.