A self-hosted video streaming server with a gallery landing page, HTML5 player, YouTube-style Stats for Nerds, and a built-in network speed test.
- Gallery — responsive dark-themed grid of video cards with real thumbnails extracted from each video
- Streaming player — HTTP range-request streaming so seek/scrub works at any point without buffering the whole file
- Stats for Nerds — YouTube-style overlay showing live resolution, buffer health, dropped frames, network throughput chart, file size, host, and playback rate. Toggle with the
Skey or the Stats button. - Speed Test — measures latency (ping/jitter), download speed, and upload speed between the browser and the server, with live sparkline charts and a video quality rating
- True 4K content — Big Buck Bunny 4K (3840×2160) plus 1080p open-source Blender films
| Layer | Choice |
|---|---|
| Backend | Node.js + Express |
| Frontend | Vanilla HTML / CSS / JS (no build step) |
| Streaming | HTTP range requests (206 Partial Content) |
| Thumbnails | ffmpeg frame extraction |
video-server/
├── server.js # Express server — streaming, API, speed test endpoints
├── package.json
├── public/
│ ├── index.html # Gallery landing page
│ ├── player.html # Video player + Stats for Nerds overlay
│ ├── speedtest.html # Network diagnostics page
│ ├── app.js # Gallery logic
│ ├── player.js # Player + stats collection
│ ├── speedtest.js # Ping / download / upload test logic
│ └── styles.css # All styles (gallery, player, stats panel, speed test)
├── thumbnails/ # JPEG thumbnails (one per video, extracted by ffmpeg)
└── videos/ # MP4 video files (not committed — download separately)
- Node.js 18+
- ffmpeg (for thumbnail extraction)
- curl or wget (for downloading videos)
git clone https://github.com/YOUR_USERNAME/video-server.git
cd video-server
npm installThe video files are not committed to the repo (too large). Download the open-source Blender films:
# Big Buck Bunny (720p, ~59 MB)
curl -L -o videos/big-buck-bunny.mp4 \
"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
# Elephants Dream (720p, ~45 MB)
curl -L -o videos/elephants-dream.mp4 \
"https://archive.org/download/ElephantsDream/ed_1024_512kb.mp4"
# Tears of Steel (720p, ~73 MB)
curl -L -o videos/tears-of-steel.mp4 \
"https://archive.org/download/Tears-of-Steel/tears_of_steel_720p.mp4"
# Cosmos Laundromat (1080p, ~146 MB)
curl -L -o videos/cosmos-laundromat.mp4 \
"https://archive.org/download/cosmos-laundromat-first-cycle/Cosmos%20Laundromat%20-%20First%20Cycle.mp4"
# Big Buck Bunny 4K UHD (3840×2160, ~615 MB)
curl -L -o videos/big-buck-bunny-4k.mp4 \
"https://archive.org/download/big-buck-bunny-4k-60fps/BigBuckBunny4k60fps.ia.mp4"ffmpeg -i videos/big-buck-bunny.mp4 -ss 00:01:30 -vframes 1 -vf scale=640:360 -update 1 thumbnails/big-buck-bunny.jpg -y
ffmpeg -i videos/elephants-dream.mp4 -ss 00:01:00 -vframes 1 -vf scale=640:360 -update 1 thumbnails/elephants-dream.jpg -y
ffmpeg -i videos/tears-of-steel.mp4 -ss 00:01:30 -vframes 1 -vf scale=640:360 -update 1 thumbnails/tears-of-steel.jpg -y
ffmpeg -i videos/cosmos-laundromat.mp4 -ss 00:01:00 -vframes 1 -vf scale=640:360 -update 1 thumbnails/cosmos-laundromat.jpg -y
ffmpeg -i videos/big-buck-bunny-4k.mp4 -ss 00:01:30 -vframes 1 -vf scale=640:360 -update 1 thumbnails/big-buck-bunny-4k.jpg -ynpm start
# Server starts at http://localhost:3000| Endpoint | Description |
|---|---|
GET /api/videos |
JSON list of all video metadata (title, file, thumbnail, resolution, fileSize, available) |
GET /videos/:file |
Streams a video file with range-request support |
GET /thumbnails/:file |
Serves a thumbnail image |
GET /api/speedtest/ping |
Returns {ts, server} for latency measurement |
GET /api/speedtest/download?mb=N |
Streams N MB of test data (max 100 MB) |
POST /api/speedtest/upload |
Accepts a body and returns {bytes, duration} |
Open any video and press S (or click the Stats button in the top bar) to toggle the overlay. It updates every second and shows:
| Stat | Source |
|---|---|
| Resolution | video.videoWidth × videoHeight |
| Viewport | window.innerWidth × innerHeight |
| Playback Rate | video.playbackRate |
| Buffer Health | Seconds of video buffered ahead of current position |
| Throughput | Bytes received via PerformanceObserver → Mbps, color-coded green/yellow/red |
| Frames | video.getVideoPlaybackQuality().totalVideoFrames |
| Dropped | droppedVideoFrames (red if > 0) |
| File Size | From /api/videos metadata |
| Host | window.location.host |
A live sparkline chart shows throughput history over the last 60 seconds.
- Drop an
.mp4file intovideos/ - Extract a thumbnail with ffmpeg
- Add an entry to the
VIDEO_METADATAarray inserver.js - Restart the server
Videos are open-source works by the Blender Foundation, licensed under Creative Commons. Server code is MIT.