A crappy website I vibe coded just to watch the hermitcraft + gamers outreach charity stream
  • JavaScript 63.9%
  • CSS 21.8%
  • HTML 6.5%
  • Nix 6.1%
  • Dockerfile 1.7%
Find a file
paul 89fcbe973d Focus and Fullscreen modes fix
1. Making it so focus mode can be resized and the size is stored.
2. Allowing the arrow keys to rotate through streams in focus and
fullscreen mode
2026-04-12 15:44:30 -04:00
src Focus and Fullscreen modes fix 2026-04-12 15:44:30 -04:00
.dockerignore first commit 2026-04-12 13:18:41 -04:00
.gitignore first commit 2026-04-12 13:18:41 -04:00
docker-compose.yml first commit 2026-04-12 13:18:41 -04:00
Dockerfile first commit 2026-04-12 13:18:41 -04:00
flake.nix first commit 2026-04-12 13:18:41 -04:00
index.html Many fixes 2026-04-12 15:38:41 -04:00
package-lock.json first commit 2026-04-12 13:18:41 -04:00
package.json first commit 2026-04-12 13:18:41 -04:00
README.md Remove placeholder image 2026-04-12 13:20:12 -04:00
shell.nix first commit 2026-04-12 13:18:41 -04:00
vite.config.js first commit 2026-04-12 13:18:41 -04:00

MultiStream

Watch multiple Twitch and YouTube streams at the same time in a single browser tab.

Features

  • Multi-stream grid — watch Twitch channels and YouTube videos side-by-side
  • Drag to reorder — grab the ⠿ handle on any stream card and drag it into position
  • Per-stream audio controls — mute/unmute each stream individually
  • Solo mode — focus audio on one stream and silence the rest with a single click
  • Global mute / unmute — silence or restore all streams at once (M / U)
  • Streams start muted — new streams are added silently so you stay in control
  • Account sign-in — sign in to Twitch and YouTube so embedded players recognise your subscriptions and YouTube Premium (skips ads)
  • Profiles — save named groups of streams (e.g. "Valorant", "Hermitcraft") and switch between them instantly
  • Persistent state — open streams, layout, and profiles are saved to localStorage and restored on reload
  • Clear All — remove all active streams in one click
  • Flexible layouts — auto-fill grid or fixed 14 column layouts

Supported URL formats

Platform Example
Twitch channel twitch.tv/channelname
YouTube video youtube.com/watch?v=VIDEO_ID
YouTube short URL youtu.be/VIDEO_ID
YouTube live youtube.com/live/VIDEO_ID
YouTube embed youtube.com/embed/VIDEO_ID

Twitch embeds require the page to be served over HTTP/HTTPS — they will not work when opened as a local file:// URL.

Keyboard shortcuts

Key Action
A Open the Add Stream dialog
M Mute all streams
U Unmute all streams
Esc Close any open dialog

Getting started

Prerequisites

  • Node.js 18 or later (24 recommended)
  • npm 9 or later (bundled with Node.js)

Install and run

# 1. Install dependencies
npm install

# 2. Start the dev server (hot reload at http://localhost:5173)
npm run dev

Available npm scripts

Script Description
npm run dev Dev server with hot reload → http://localhost:5173
npm run build Production bundle → dist/
npm run preview Serve the production build → http://localhost:4173
npm start Build then serve (production preview)

Nix

Two Nix entry points are provided — use whichever matches your setup.

shell.nix — classic Nix (nix-shell)

Uses your system nixpkgs channel.

# Enter the dev environment (auto-installs npm deps on first run)
nix-shell

# Or run a single command without entering the shell
nix-shell --run "npm run build"

flake.nix — modern Nix (nix develop)

Pins nixpkgs to nixos-unstable for fully reproducible builds.

# Enter the dev environment
nix develop

# Or run a single command
nix develop --command npm run build

Both shells provide Node.js 24 (with npm) and print available commands on entry. If node_modules/ is absent they run npm install automatically.

Reproducible nix build

flake.nix also exposes a packages.default output that builds the dist/ folder hermetically via buildNpmPackage. Before using it you need to supply the correct npmDepsHash:

# 1. Make sure package-lock.json is up to date
npm install --package-lock-only

# 2. Compute the hash
nix-prefetch-npm-deps package-lock.json

# 3. Paste the printed hash into flake.nix → npmDepsHash = "sha256-...";

# 4. Build — outputs to ./result/
nix build

Docker

Quick start

# Build the image and start the container
docker compose up -d

# Tail logs
docker compose logs -f

# Stop and remove the container
docker compose down

The app will be available at http://localhost:3000.

Build and run manually

# Build the image
docker build -t multistream:latest .

# Run the container
docker run -d \
  --name multistream \
  -p 3000:3000 \
  --restart unless-stopped \
  multistream:latest

Image details

The Dockerfile uses a two-stage build to keep the final image small (~173 MB):

Stage Base image Purpose
builder node:24-alpine Install npm dependencies and run vite build
runner node:24-alpine Install serve, copy dist/, expose port 3000

The runner stage contains only the compiled static assets and the static file server — no source code, Vite, or dev tooling.

Changing the port

Edit docker-compose.yml and update the left side of the port mapping:

ports:
  - "8080:3000"   # now reachable at http://localhost:8080

Or pass it directly with docker run:

docker run -d -p 8080:3000 multistream:latest

Rebuilding after source changes

docker compose up -d --build

Project structure

multistream/
├── index.html          # HTML shell (markup only)
├── src/
│   ├── main.js         # Application logic (ES module)
│   └── style.css       # All styles
├── public/             # Static assets served as-is
├── dist/               # Production build output (generated)
├── package.json        # npm scripts and dependencies
├── vite.config.js      # Vite configuration
├── shell.nix           # Classic Nix dev shell
├── flake.nix           # Flake-based Nix dev shell + build
├── Dockerfile          # Two-stage Docker build
├── docker-compose.yml  # Docker Compose service definition
└── .dockerignore       # Files excluded from Docker build context

Dependencies

Package Role
SortableJS Drag-and-drop reordering of stream cards
Vite Dev server and production bundler
serve Static file server used in the Docker image