// hero.sequencecoherence 100% · glyphs 0 · t 0.00s

Almere, NL · CET

DominiqueKarreman

Integration Engineer

I engineer software the way a cinematographer lights a scene — everything invisible, nothing accidental.

Scroll

Dominique Karreman

integration engineer · almere, nl · cet

Dominique Karreman in profile, arms folded, the figure rendered from the digits 0 and 1.
Reassembly — 000%

Most software fails at the boundaries — where one system hands off to another, where an assumption that held on one side quietly stops being true on the other. That is the work I have spent my time on: making systems that were built separately, by different people, at different times, behave as though they were designed together.

I came to engineering through photography. Cinematography teaches you that a frame is a set of decisions — what is in focus, what is exposed, what is left out of shot — and that the best of them are the ones nobody notices. The structure should look obvious in hindsight and unremarkable in use.

I build across the stack: C# and Python where systems talk to each other, Swift when it has to be native, React and Laravel when it has to ship. Four runtimes, one engineer — which mostly means I have learned what each of them is bad at.

Based
Almere, Netherlands · CET / CEST
Focus
Systems integration, distributed state, and the boundaries between them
Currently
Placeholder — what are you building or learning right now?
Open to
Conversations. Not actively looking.
  • Photography

    Where the eye was trained. Light before code.

  • Cinematography

    Composition as a form of engineering.

  • Basketball

    Systems that only work if five people read the same play.

  • Piano

    Structure you have to feel rather than reason about.

EXHIBITION FLOOR · 04 MACHINES

// selected work

Four systems that had to hold when the systems around them didn’t.

An exhibition floor in a dark building: four machines on a bench, one lamp over each. The room stays unlit until you point at something.

Move over a machine to switch its light on

Four systems I am trusted with, and what that has actually meant.

// capabilities

01 / INTEGRATION

Making systems agree

Contract design, idempotency, retry and backoff, dead-letter handling, and the unglamorous work of reconciling two sources that both believe they are authoritative.

  • REST
  • Webhooks
  • Queues
  • ETL
  • OAuth

02 / BACKEND

Services that stay up

Domain modelling, transactional boundaries, and knowing which consistency guarantee a feature actually needs rather than defaulting to the strictest one available.

  • C#
  • .NET
  • Python
  • Laravel
  • Postgres

03 / INTERFACE

Front ends that hold up

State that is honest about being asynchronous, rendering budgets treated as a constraint rather than an afterthought, and interaction that survives a slow connection.

  • React
  • TypeScript
  • WebGL
  • Canvas

04 / NATIVE

Going down to the metal

When the platform layer stops being enough — on-device processing, camera and sensor pipelines, and the frame budget that comes with them.

  • Swift
  • Metal
  • iOS

Reading about integration is dull. Break one instead.

// live — try it

Delivered
0
Dead-letter
0
Queued
0
Retries
0

Kill the transform node and the pipeline does not lose your message — it applies backpressure, queues, and retries with exponential backoff until the node returns. Malformed payloads never reach it at all; validation diverts them to the dead-letter queue where they can be inspected instead of silently dropped.

Three things I will argue about.

// philosophy

  1. 01

    The boundary is the system.

    Teams spend their effort on what happens inside a service and their outages on what happens between them. I would rather over-invest in the contract, the failure mode, and the retry semantics than in the elegance of code nobody else will call.

  2. 02

    Performance is a design decision, not a phase.

    Budgets set at the start are constraints that shape the work. Budgets discovered at the end are bugs with a deadline attached. The sequence at the top of this page is 2.7 MB because that number was chosen before it was built, not measured after.

  3. 03

    Prefer the boring mechanism.

    A queue and a retry will outlive a clever abstraction. Most of what I have shipped that still runs is unremarkable on purpose — the interesting decision was usually choosing not to build the interesting thing.

Two bugs from this page, and what they cost.

// source

A seek that could never complete

The scroll-scrubbed sequences were guarded by a boolean set before each seek and cleared on the seeked event. When a seek is issued before the video has data that event never fires — so the flag stayed true and the scrubber was dead for the rest of the session. Trusting the browser’s own state removes the entire class of bug.

index.html — createScrubber()JS
/* Deliberately NOT guarded by a boolean "seeking" flag. A seek issued
   before the video has data never fires `seeked`, and a flag set on the
   way in then never clears -- one dropped event and the scrubber is dead
   for the rest of the session. Trust the browser's own video.seeking and
   re-check next frame, so a dropped seek simply gets reissued. */
function syncSeek() {
  if (!duration) return;
  const t = smooth * duration;
  if (Math.abs(t - video.currentTime) <= 1 / 48) { stalled = 0; return; }

  if (!video.seeking) {
    if (video.currentTime === lastAt && ++stalled > 90) return;
    lastAt = video.currentTime;
    video.currentTime = t;
  }
  if (!seekRaf) seekRaf = requestAnimationFrame(() => { seekRaf = 0; syncSeek(); });
}

An animation that prevented its own trigger

Every entrance on this page starts clipped to zero height. IntersectionObserver measures its ratio after clipping, so the ratio was permanently zero and a threshold-based observer could never fire. The reveal was hiding itself from the thing meant to reveal it.

index.html — revealsJS
/* threshold MUST stay 0. The resting state clips the element to zero
   height, and IntersectionObserver measures ratio after clipping -- so
   any non-zero threshold can never be met and the reveal never fires.
   isIntersecting is still true, so set the trigger with rootMargin. */
const io = new IntersectionObserver((entries) => {
  for (const e of entries) {
    if (!e.isIntersecting) continue;
    e.target.classList.add('is-in');
    io.unobserve(e.target);          // an entrance replayed is noise
  }
}, { threshold: 0, rootMargin: '0px 0px -14% 0px' });

Code you can read rather than take my word for.

// github

Colophon — measured live in your browser

Document
HTML + CSS + JS
KB
Media
fetched so far
KB
Largest paint
MS
DOM nodes
Dependencies
1 THREE.JS

No framework, no font service, no analytics. One dependency — three.js, self-hosted and loaded only for the exhibition scene, on pointer-capable desktop. Two scroll-scrubbed H.264 sequences, one shared scrubber, and a canvas. The numbers above are read from the Performance API on this page load rather than typed in by hand — if they get worse, this section says so. Media keeps counting as you scroll, because the sequences are fetched only when you approach them; a figure captured at load would have flattered the page by several megabytes.