vod Jam and Earl#
You've been tasked with transporting the VODs back from AtmosphereConf when something goes wrong with your ship. Your new task is to collect all the VODs as quickly as possible so they can be streamed!
Live at https://vod.atverkackt.de
This initially completely ripped off @goose.art but hopefully has diverged sufficiently. It is 99% coded by Copilot, assets are fetched from the internet and referenced below. Ping me if you spot any issues.
Running#
npm install
npm run dev
Build for production:
npm run build
npm run preview
How It Works#
Video data#
Videos are fetched from across the AT Protocol network via the UFOs API, which indexes all place.stream.video records. Schedule metadata (talk titles, speakers, descriptions) is pulled from community.lexicon.calendar.event records on atmosphereconf.org's PDS. Playback uses HLS via hls.js with playlists served by vod-beta.stream.place.
Game#
The player explores a tilemap world as Earl, moving with keyboard (WASD / arrows) or on-screen controls. VODs appear as presents scattered across the map. Walking up to a VOD shows a detail card with talk info, speaker avatar, and thumbnail; from there you can collect the VOD or watch it inline. A timer tracks how long it takes to find them all.
State (collected VODs, timer, player position) is persisted to localStorage so progress survives page reloads.
AT Protocol integration#
Players can sign in with their Bluesky account via AT Protocol OAuth (@atproto/oauth-client-browser). Collected VODs are written to the de.atverkackt.vod-jam.collected collection, and completion times to de.atverkackt.vod-jam.completed. The leaderboard reads these records back from UFOs, deduplicated by DID and sorted by fastest time.
Visual style#
- Fonts: Two colour-font variants of the ToeJam & Earl typeface by Patrick H. Lauke, animated with a CSS
font-switchkeyframe. - Borders: Procedurally generated spikey/jagged rectangular borders, seeded by a string so every instance is unique but deterministic. Built from triangle-wave spikes rendered as CSS
clip-path: polygon()plus a matching SVG stroke. - Sprites: Earl walking/idle/floaty sheets, presents, ship, terrain tiles (grass variants + trees), and pixelated 24×24 speaker avatar sprites.
- Theme: Deep space colour palette (see
src/lib/theme.ts). - Cursor: An animated ToeJam sprite replaces the default cursor.
- Audio: Three tracks —
funkotronic-beat.mp3(intro),jammin.mp3(gameplay),rocket-racket.mp3(completion).
Project Structure#
| Path | Description |
|---|---|
src/ |
SvelteKit app (Svelte 5, static adapter) |
src/lib/game/ |
Game engine — world map, player, presents, HUD, input, game loop |
src/lib/ |
Shared components — video player, wavy borders, API client, OAuth, leaderboard |
src/routes/ |
Pages: main game (/), leaderboard (/leaderboard) |
static/ |
Sprites, fonts, audio, and other static assets |
scripts/ |
Asset pipeline — sprite extraction & speaker avatar generation |
infra/ |
AWS CDK stack (S3 + CloudFront + Route 53) and deploy script |
atmosphereconf/ |
Separate Astro site for the AtmosphereConf conference website |
tests/ |
API load tests and stream diagnostics |
Scripts#
# Generate pixelated 24×24 speaker avatar sprites from Bluesky profiles
npm run generate-sprites
Tests#
npx tsx tests/load-test.ts # API fetch + content validation
npx tsx tests/stream-test.ts # HLS stream availability, codecs, bandwidth
Credits & Resources#
- Original code inspiration: @goose.art
- Fonts: ToeJam & Earl colour font variants by Patrick H. Lauke
- Sprites: The Spriters Resource — ToeJam & Earl (Sega Genesis)
- Boris sprite: created by my daughter with her mad artist skillz
- Music: archive.org
- Video playback: hls.js
- Video index: UFOs API by Microcosm
- AT Protocol OAuth: @atproto/oauth-client-browser
- Conference data: AtmosphereConf