Skip to main content

2026-21

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.sh that drops .txt next 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-failure is a trap for services that exit cleanly when they fail. Use Restart=always for 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