2026-21
Screenshots auto-append to 2026 21 Raw — review those for memory jogs, write here for publishing.
What happened this week
Spun up a personal cable channel at tv.tools.ejfox.com — every visitor sees the same YouTube video at the same second, computed deterministically from a fixed epoch and the sum of video durations. No database, no per-client state, just a small Deno app on smallweb. Enriched 507 Watch Later URLs to 475 alive videos for ~224 hours of looping runtime. Same week I rescued the radio station (Restart=on-failure quietly let liquidsoap stay dead overnight), wired live "N other viewers/listeners" counters on both, and documented a Pi 5 kiosk recipe for the wall TV. Burned $19 of RunPod on a forgotten pod — same exact failure mode as the dead radio service, twice in one quarter. Note to future me: Restart=always everywhere.
What I made / shipped
- tv-station — public repo, MIT, at github.com/ejfox/tv-station. README is a recipe book — anyone with a Debian VPS can run their own public-access channel from a Watch Later export.
- Live viewer/listener counters on tv + radio. Polling-based, sessionStorage UUID, 30s TTL, no websockets. Elegant pulsing teal pip top-left, says "just you" when you're alone.
- Screenshot embeddings WebGL atlas — 1554 screenshots, SigLIP visual + MiniLM OCR embeddings, force-atlas layout seeded from UMAP, fisheye cursor lens, LOD swap to hi-res at deep zoom, rogue's gallery contact sheets per cluster. Parquet backed up to R2.
- TP-7 transcription pipeline — whisper-cpp + ggml-tiny + ffmpeg pre-conversion, wrapper at
~/bin/transcribe-recordings.shthat drops.txtnext to each recording. - Uptime Kuma audit — added 12 missing monitors, hardened tv-station with per-IP token bucket (60/min, burst 30), ETag on
/playlist.json, 5s cache on/now.
What I learned
- "Quantity is quality" — biggest take from Wilson Lin's solo 3B-embedding search writeup. Crawl completeness beat embedding sophistication for him; for me that means an automated daily embedding pass on new screenshots matters more than swapping SigLIP variants.
- HNSW is immutable, requires full-RAM rebuild on inserts. Wilson sharded across 64 nodes to work around it. At 1.5k items I'm staying brute-force forever — the lesson is to plan for incremental updates from day one if I ever scale up.
- Self-hosted GPU compute for embeddings is a pricing error — OpenAI batch API would've been ~100× cheaper than Wilson's RunPod fleet. My $19 incident agrees. The text half of any future embedding pipeline goes to a hosted API.
- Semble (BM25 + CPU embeddings, MCP server) saves ~70% of the tokens grep+read would burn for code search. Two queries in tv-station found the rate-limit middleware and the viewer-counter block on the first hit with full context.
- term.everything is beautiful and not for me — it's a Linux-only Wayland compositor that renders GUI apps into a terminal. The dream was Firefox in a tmux pane, but my whole stack is the opposite direction — I replaced GUI apps with TUIs on purpose. Save the demo, skip the install.
Restart=on-failureis a trap for services that exit cleanly when they fail. UseRestart=alwaysfor anything that should never be down.
📚 Media consumed
- Wilson Lin's solo-built embedding search engine writeup (blog.wilsonl.in/search-engine) — single best technical post I read this week
- A nicer voltmeter clock (lcamtuf) — analog voltmeter needles for hour/minute, CRT-era hardware-hack porn
- Skimmed the 2B2T 1M-block-world archive release — Minecraft anarchy server archaeology is its own genre now