Personnel Records Interface

Jason Jamieson

Principal Software Engineer · Kotlin · Kotlin Multiplatform · Go

◂ Log Entries

Why this site is no longer a canvas

This site used to be a Compose Multiplatform app compiled to WebAssembly. It was a scrollable column of category rows, each one a horizontal strip of cards that resized when you clicked them. It looked good. It was also the wrong tool, and it took me a while to see why.

The canvas problem

Compose Multiplatform for Web doesn’t render HTML. It renders into a single <canvas> element via Skia. Everything you see is pixels the runtime painted.

That one architectural fact cascades:

  • Search engines find nothing to index. There is no text in the document.
  • Screen readers get almost nothing, because there are no semantics to read.
  • Ctrl+F doesn’t work. Neither does selecting a paragraph to quote it.
  • The first paint waits on a multi-megabyte runtime download.

None of that matters for an internal dashboard behind a login. All of it matters for a personal site whose entire purpose is being found and read.

The test I should have applied earlier

Is the thing a document or an application?

Documents want the DOM — text, links, semantics, indexability. Applications can justify a canvas. A blog is a document. Figma is an application. Most things people build are documents wearing an application costume, and this site was one of them.

The theme argued against it too

I wanted a heavily styled terminal aesthetic — green phosphor, scanlines, the whole Weyland-Yutani look. My instinct was that a custom visual identity needed a canvas to draw on.

That’s backwards. Scanlines are a repeating-linear-gradient. Phosphor glow is a text-shadow. The terminal look is typography and colour, which is what CSS does natively:

.crt::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0.22) 0 1px,
    rgba(0, 0, 0, 0) 1px 3px
  );
}

Fifteen lines, and it renders on top of real text. The aesthetic became a layer over the content instead of a replacement for it.

What Kotlin/Wasm is actually good at

Worth saying clearly, because the conclusion isn’t “the technology is bad.”

Kotlin/Wasm targets WasmGC, so it reuses the browser’s garbage collector rather than shipping its own. That makes it a genuinely good way to run Kotlin logic in a browser — a parser, a solver, a simulation, a domain layer shared with an Android app. Compute, not UI.

The narrower thing is Compose for Web specifically: it exists so that a team with an existing Compose app can ship a web build cheaply. That’s a real and valuable use case. It just isn’t a blog.

What carried over

Less was wasted than I expected. The GitHub Pages deploy pipeline transferred almost unchanged — same Actions workflow shape, same custom domain, same DNS. The theming work turned into CSS custom properties. And the writing I’d been putting off is now a markdown file instead of a Kotlin data class, which is probably the change that matters most.