Things I've made.
A product I ship and run, the agent pipelines that move it forward, and a workshop of small tools I built because I wanted them. Further down: the way I work, wired into the page you're reading.
-
When.
A private period tracker that lives in the calendar you already check (Google, Notion, or CalDAV), not one more app to open. Log the day a period starts; it learns your cycle and drops each predicted phase onto your calendar. Built and run with Anna, end to end.
→ getwhen.net · live · €4/mo after a 14-day trial -
Agentic delivery pipelines
Multi-agent LLM pipelines that move a product forward on a loop: plan, build, review, verify. Each stage is an explicit step, with review gates that have to pass before code merges. The orchestration is ordinary deterministic code; the agents do the fuzzy work inside it. Fan out many agents to cover ground, keep the few results that hold up. The payoff is a small team shipping like a bigger one, with the same care on every change.
→ orchestration · review gates · continuous delivery -
baker
Single-binary static site bundler and HTTP server in Zig. Brotli + gzip, ETags, 304s, hot reload via SIGHUP. Bakes a site into one
→ codeberg.org/litozor/baker · Zig.binfile, then serves it. -
zremdic
A small, latency-focused key-value cache served over UDP, in Zig. One datagram per lookup, a sharded store with a worker per core, atomic ops with exactly-once semantics over an unreliable transport, TTLs and LRU eviction. ~210k ops/sec at ~19μs on loopback. A cache, not a database: values live in memory only.
→ codeberg.org/litozor/zremdic · GitHub · Zig -
zhecs
An entity component system in Zig, small enough to read in an afternoon. Plain Zig types as components, no registration; matching entities stored together in contiguous columns. Systems run in ordered phases; relationships, singletons, and observers when you want them. A million entities stepped in ~19 ms per chunk. Examples wired to raylib, Bullet, and SDL.
→ codeberg.org/litozor/zhecs · GitHub · Zig -
wiki-art-downloader
Bulk-downloads an artist's catalogue from WikiArt as JPGs. Node 18+, zero deps. Walks the eight CDN shards in parallel, with dry-run and resume-by-skip.
→ codeberg.org/litozor/wiki-art-downloader · Node -
ssw
Multi-tab state, no ceremony. SharedWorker as the single source of truth, optimistic per-tab mirror, batched ack protocol. ~3.5 KB gzipped, tested against real MessageChannels.
→ codeberg.org/litozor/ssw · TypeScript -
swr
A SharedWorker HTML renderer on top of ssw. Views are functions that return HTML strings; the render runs once in the worker and broadcasts to every tab, skipping identical output so no tab repaints for nothing. Per-view error isolation, keyed-list memoization, and a tab-side runtime that's just a port and an
→ codeberg.org/litozor/swr · GitHub · TypeScriptinnerHTML. ~65 integration tests. -
number-helper-extensions
Ergonomic helpers on
→ codeberg.org/litozor/number-helper-extensions · JSNumber:(6).minutes(),(3).times(i => i*2),clamp,ordinal,ago()/fromNow(). The prototype is only touched if you callextendNumber(); every helper is also exported raw.
The rest of the workshop lives at codeberg.org/litozor, backed up to GitHub when it's working.
The way I work.
Five habits I keep coming back to in serious work. Each one is wired into the site you're reading: the column on the right is the receipt, pulled straight from the source. Open devtools any time and check.
I build in rings.
I split a system into rings: the most important things in the middle, the optional things on the outside. You should be able to peel off any feature without breaking the core. This site is wired exactly that way: a tiny kernel at the center, mode renderers in the middle, optional features at the edge.
The bus doesn't know about modes; modes don't know about features; features don't know about each other. Coupling is what makes systems brittle; the boundary is where I draw the line.
assets/ ├── kernel/ bus, router, mode ├── modes/ terminal · swiss ├── features/ indicator · snippets │ banner · keys · sim └── tokens/ palette + base
Every byte earns its place.
I treat dependencies and code lines like ingredients: every one has to earn its spot. This site has no third-party libraries, no build step, and every file stays small enough to read in one sitting.
Constraints aren't about being a purist; they're how I keep the codebase legible six months later. First time I cut a feature to keep a file small, I realized I didn't need it. Fast is a side-effect of small.
- deps
- 0
- files
- 16
- longest
- 149 lines
- average
- 61 lines
- fonts
- system
- build
- none
Source is the product.
Open devtools right now. The HTML, CSS, and JavaScript you'll see is the same code I edited. No compiler in between, no Shadow DOM hiding things. Every file opens with a short comment naming what it's for, what it depends on, and what it deliberately won't do.
The reader I'm writing for is a senior engineer poking around for ten minutes. Comments explain why something is the way it is; names handle the what.
/* * mode-manager.js - owns <html data-mode>. * Wraps swap in a View Transition with * reduced-motion fallback. Emits 'mode:change'. * Dependencies: ./event-bus.js * Invariants: data-mode set inline before paint. * Non-goals: no UI; no scheduling. */
Numbers over adjectives.
"Faster" is unfalsifiable. "P99 dropped from 412ms to 89ms after dropping the N+1 query" isn't. When I claim something about performance, I want a number behind it and a script that reproduces it. Otherwise the optimization didn't really happen.
This site has a budget too: the table on the right. They're not impressive numbers, just ones I picked on purpose and then held myself to.
| metric | target |
|---|---|
| first paint | < 100 ms |
| transfer | < 50 KB |
| CLS | 0 |
| INP | 1 frame |
| fonts | system |
Code is for the next reader.
In three months I'll read this code as a stranger. Header comments, honest names, and notes about why are the breadcrumbs I leave for that stranger.
Files stay small enough to hold in one head. When a file gets fat, split it; don't compress it.
/* * file.js - one-line purpose. * Dependencies: ... * Invariants: ... * Non-goals: ... */
Habits I keep coming back to.
-
Signal-style bus
Topics remember their last value. Late subscribers replay. Init order stops mattering.
→ kernel/event-bus.js -
View Transition + motion fallback
Feature-detect
→ kernel/mode-manager.jsstartViewTransitionand respectprefers-reduced-motionin four lines. -
Registry of renderers
One shell per element, mode-keyed renderer per shell. Add a mode without touching the shell.
→ modes/components.js -
Inline pre-paint script
Set the theme attribute on
→ <head> of every page<html>before any stylesheet loads. FOUC dies.
Things I tried and dropped.
Six decisions on this site that lost. The version that shipped is better because of the version that didn't.
-
Separate tags per mode.
First sketch had
<terminal-header>and<swiss-header>as distinct elements; the router would swap tags on mode change. Killed it the first time I switched modes mid-interaction: swapping tags throws out the DOM node, so focus, scroll position, and any in-flight transition reset with it. Now there's one<site-header>shell and a per-mode renderer registry; the element survives the swap, only its innards change. -
One renderer with
if (mode === ...)branches.Considered a single mode-aware function per shell. Dropped it because adding a third mode would mean editing every renderer: modes coupled through every shell. The registry inverts that: each mode owns its own file and registers itself. (Adding Blueprint mode later is a new file, not a sweep through old ones.)
-
Shadow DOM.
The obvious choice for custom elements. Pulled it out anyway. The page told one story and devtools told another, and the thesis of this site is that the source is the product. View-source has to match what's actually rendering. Renderers now write straight to
host.innerHTML. -
A Proxy-based observable store.
Started sketching one for cross-component state and stopped about thirty lines in. The actual need was "late subscribers should see the current value." The event bus solves that in forty lines by replaying the last published value to new subscribers in a microtask. (The fancier option was solving a problem I didn't have.)
-
Mode from
prefers-color-scheme.The first cut had no toggle: read
matchMediaand pick terminal or swiss off the OS theme. But the modes aren't dark vs light; they're different readings of the page, which the OS theme doesn't control. A?mode=URL param came next: shareable, but it doesn't survive a click to the next page and clutters every link. Now an explicit choice persists tolocalStorage, and an inline<head>script replays it before first paint so the switch never flashes. -
Project content as a JSON manifest.
Card data started as one
content.json: title, summary, tags, body as an HTML string. Editing prose inside JSON strings is miserable, so I tried the other direction: a markdown file per project with frontmatter and a hand-written parser, ~90 lines, not CommonMark. But for the dozen hand-curated cards this site actually has, that meant a loader, a parser, and acontent/tree standing between me and a list that fits on one screen. Dropped the whole pipeline. Each project is now plain HTML in the page, andCtrl+Ushows the card exactly as written.
Where I've put this to work.
-
Sensor fleets at volume
Ingestion for 100,000+ sensors, each reporting every few minutes: tens of millions of data points a day, sustained, zero dropped readings, no backlog.
→ 100k+ devices · ~tens of M points/day -
LoRaWAN, end to end
Gateways, device provisioning, network-server integration: the full path from radio to database. The edges you only hit running a live fleet, not reading the spec.
→ field-deployed, full stack -
AWS in production
Compute, storage, queues, and the pipelines between them: production systems I own from the instance up. The fleet above runs here.
→ years in production -
Data analysis on the stream
Sensor-signal analysis on the telemetry above; document and word-frequency analysis on text corpora; sentiment analysis on social-media streams.
→ sensor · text · social sentiment -
Networking & bare metal
CCNA1-level networking fundamentals and end-to-end physical machine setup: rack, OS, network, runtime. The radio→DB path starts at hardware I provisioned.
→ CCNA1 · bare-metal → runtime -
Games · FPS and MMORPG
First-person titles and MMORPG servers shipped: a 16 ms client budget on one side, thousands of concurrent players holding state on the other. Engine code and gameplay scripting.
→ FPS · MMORPG · engine
If a team treats the work like this, I'd love to talk. About · linh@lelinh.dev · codeberg.org/litozor · GitHub (backup, when it's working) · LinkedIn