<sky-template path="_imports/main.html" /><sky-block name="content"><section class="hero"><p class="eyebrow">Hi 👋</p><h1> I'm Michael Sendker, a software developer from The Other Bay Area (Tampa Bay).</h1><p class="lede"> I find the pain points in how companies build and test their software, and engineer them away. If you want to know more about me, check my<a href="/about">about page</a>, or look through my<a href="/projects">projects</a>.</p><p> If you want to see about hiring me, and my one man developer agency Standing Water LLC, please email me: m at standingwater dot io.</p></section><section class="story"><h2>How this site is built</h2><p> standingwater.io has had a few lives. The first two were Gatsby sites: one started from an HTML5 UP template, then a playful, colorful version I built from scratch.</p><p> In May 2022, my dog Sky died. The day she passed, I sat down to write about her, and Gatsby's development server refused to start. I wrote the post anyway, pushed it, and hoped Netlify would build it. It did, but I was done fighting broken dependencies just to write. So I wrote my own static site generator, modeled on the wonderfully simple<a href="https://sergey.cool/">Sergey</a>, and named it bluesky, after her. I started it in C, switched to C++ after two weeks of struggling, and by September 2022 this site was running on it.</p><p> I've been fascinated by parsers ever since I fumbled my way into hand-writing them, with no idea what I was doing, to process texts for<a href="https://github.com/Anno-Wiki">anno.wiki</a>. In 2023 I had an idea for a language: C, plus the smallest possible version of classes. Structs get methods, and <code>.</code> and <code>-></code> mean the same thing. I called it c′ (c-prime: the derivative of C, so its files end in <code>.dcdx</code>).</p><p> I always meant to build it myself. I read <em>Crafting Interpreters</em>, and bought <em>Engineering a Compiler</em> and the dragon book. I spent a year wanting to learn compilers and performance properly, and yak shaved my way into the math underneath it all with<a href="https://github.com/mas-4/cheap-experiments">cheap experiments</a> instead of just focusing on compilers. Then I got nerd sniped by epic poetry and learning Latin, and the compiler books stayed on the shelf.</p><p> In 2026, after spending a lot of time using Claude Code for some new personal experiments and projects, on a lark I asked Claude to try building c′.</p><img class="time-card" src="/five-minutes-later.jpg" alt="Five minutes later..." width="960" height="540" /><p><a href="https://github.com/mas-4/cprime">cprime</a>, a c′ compiler forked from Rui Ueyama's<a href="https://github.com/rui314/chibicc">chibicc</a> that now compiles itself (the receiver ended up being called <code>ego</code>). What a let down. Here's a Hello, World:</p><figure class="code-figure"><pre class="listing"><code><sky-source path="../cprime/examples/hello.dcdx" /></code></pre><figcaption><a href="https://github.com/mas-4/cprime/blob/main/examples/hello.dcdx">cprime/examples/hello.dcdx</a></figcaption></figure><p> From there I thought, let's get it self-hosting! No sooner asked than done. Then let's rewrite bluesky in c′! Et voilà :<a href="https://github.com/mas-4/skyblue">skyblue</a>, a rewrite of bluesky in c′ 😞. (Jack Dorsey took bluesky out from under me! 🤬) No matter, her name actually was Sky Blue Sendker. But once that was done, all that was left was for it to rewrite standingwater.io in skyblue.</p><p class="punchline">🫲 Atque ecce. 🫱</p><p> This is all very bittersweet. A mountain of effort in 15 minutes to get the thing I dreamed of for 3–5 years. But we're all figuring out what we're good for these days. You can see some of my new projects on the<a href="/projects">projects</a> page. AI assisted projects are clearly marked.</p><p> So anyway, today this site is built by skyblue, which is compiled by cprime, which bootstraps itself from C on every deploy to Netlify via GitHub. Here's what's under this paragraph, from the top down. skyblue pulled each excerpt out of the real source when it built this page, and the <label for="xray" class="inline-toggle">source</label> button at the top lifts the page off the files it was made from.</p><ol class="strata"><li class="stratum"><p class="stratum-head"><span class="stratum-name">the template</span><a href="https://github.com/mas-4/standingwater-flat/blob/main/src/_imports/main.html">src/_imports/main.html</a></p><pre class="listing"><code><sky-source path="_imports/main.html" from="<main" to="</main>" /></code></pre><p class="stratum-note">This paragraph is in the <code>content</code> slot of the site's main template.</p></li><li class="stratum"><p class="stratum-head"><span class="stratum-name">skyblue, in c′</span><a href="https://github.com/mas-4/skyblue/blob/main/src/render.dcdx">skyblue/src/render.dcdx</a></p><pre class="listing"><code><sky-source path="../skyblue/src/render.dcdx" from="void Render.slot" to="}" /></code></pre><p class="stratum-note">skyblue fills the slot with this method. <code>ego</code> is the <code>Render</code> it's called on.</p></li><li class="stratum"><p class="stratum-head"><span class="stratum-name">cprime, in c′</span><a href="https://github.com/mas-4/cprime/blob/main/parse.dcdx">cprime/parse.dcdx</a></p><pre class="listing"><code><sky-source path="../cprime/parse.dcdx" from="Member *Type.method" to="}" /></code></pre><p class="stratum-note"><code>ego.slots.get(…)</code> is a method call, and this is how cprime finds the method. The compiler is written in c′ too.</p></li><li class="stratum"><p class="stratum-head"><span class="stratum-name">cprime's stage 0, in C</span><a href="https://github.com/mas-4/cprime/blob/main/bootstrap/parse.c">cprime/bootstrap/parse.c</a></p><pre class="listing"><code><sky-source path="../cprime/bootstrap/parse.c" from="static Member *get_method" to="}" /></code></pre><p class="stratum-note">The first cprime is compiled from a frozen copy written in plain C. Here's the same function, before c′.</p></li><li class="stratum"><p class="stratum-head"><span class="stratum-name">bedrock</span><a href="https://github.com/mas-4/standingwater-flat/blob/main/build.sh">build.sh</a></p><pre class="listing"><code><sky-source path="../build.sh" /></code></pre><p class="stratum-note">Netlify runs this on every push: <code>cc</code> builds stage 0, stage 0 builds cprime, cprime builds skyblue, and skyblue builds this page.</p></li></ol></section></sky-block>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><script><sky-include path="theme.js" /></script><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title><sky-slot name="title" />${meta.title}</title><meta name="description" content="${meta.description}" /><meta name="theme-color" content="#142420" /><link rel="icon" href="/favicon.svg" type="image/svg+xml" /><link rel="stylesheet" href="/style.css" /></head><body><!-- Every page is a sheet of paper lying on the source it was built from. This checkbox lifts the sheet; no JavaScript involved. --><input type="checkbox" id="xray" class="xray" aria-label="Show the source of this page" /><div class="desk"><div class="sheet"><!-- Under the paper, the page's source, for the candle to show. --><div class="underside" aria-hidden="true"><sky-sources item="underside.html" /></div><div class="paper" aria-hidden="true"></div><svg class="flame" viewBox="0 0 24 44" aria-hidden="true"><defs><radialGradient id="flame-body" cx="50%" cy="78%" r="62%"><stop offset="0" stop-color="#fffbe8" /><stop offset="0.35" stop-color="#ffe08a" /><stop offset="0.7" stop-color="#ff9a2e" /><stop offset="1" stop-color="#e2470f" stop-opacity="0" /></radialGradient><radialGradient id="flame-core" cx="50%" cy="80%" r="55%"><stop offset="0" stop-color="#ffffff" /><stop offset="0.6" stop-color="#fff3c4" /><stop offset="1" stop-color="#ffd166" stop-opacity="0" /></radialGradient></defs><path class="flame-body" fill="url(#flame-body)" d="M12 1C15 11 22 18 21 29 20 37 16 43 12 43 8 43 4 37 3 29 2 18 9 11 12 1Z" /><path class="flame-core" fill="url(#flame-core)" d="M12 15C14 21 17 26 16.5 32 16 37 14 40 12 40 10 40 8 37 7.5 32 7 26 10 21 12 15Z" /><ellipse class="flame-base" cx="12" cy="40" rx="3.2" ry="2" fill="#5aa9ff" opacity="0.45" /></svg><label for="xray" class="curl" title="Lift the page to see what it's made of" aria-hidden="true"></label><label for="xray" class="sheet-cover" title="Back to the page"></label><sky-include path="_nav.html" /><main class="page"><sky-slot name="content" /></main><sky-include path="_footer.html" /></div><aside class="ground" aria-label="The source of this page"><header class="ground-head"><p><a href="https://github.com/mas-4/skyblue">skyblue</a> built this page from these files, in the order it used them.</p><label for="xray" class="ground-close">back to the page →</label></header><sky-sources item="source.html" /></aside></div><script><sky-include path="candle.js" /></script></body></html>
// The light switch. Until it's flipped, the page follows the system's// light or dark preference (that part is CSS). Flipping it picks the other// one and remembers it. This runs in <head>, so a saved choice applies// before the page is drawn.(() => { const root = document.documentElement; const system = matchMedia("(prefers-color-scheme: dark)"); try { const saved = localStorage.getItem("theme"); if (saved === "light" || saved === "dark") root.dataset.theme = saved; } catch {} const isDark = () => (root.dataset.theme ? root.dataset.theme === "dark" : system.matches); document.addEventListener("DOMContentLoaded", () => { const button = document.querySelector(".theme-toggle"); if (!button) return; const show = () => button.setAttribute("aria-checked", String(isDark())); button.hidden = false; show(); system.addEventListener("change", show); button.addEventListener("click", () => { root.dataset.theme = isDark() ? "light" : "dark"; try { localStorage.setItem("theme", root.dataset.theme); } catch {} show(); }); });})();
// The candle. Hold the pointer over the words and a flame lights above// them: the paper warms and thins, and the page's source shows through// from underneath. Only over the letters themselves, not blank paper.// Without this script the paper simply stays opaque.(() => { const sheet = document.querySelector(".sheet"); if (!sheet || !matchMedia("(hover: hover) and (pointer: fine)").matches) return; // Is (x, y) on a letter? Find the text position under the point, then // check that the point is inside the box of the character on either side. const range = document.createRange(); const overGlyph = (x, y) => { let node, offset; if (document.caretPositionFromPoint) { const pos = document.caretPositionFromPoint(x, y); if (pos) [node, offset] = [pos.offsetNode, pos.offset]; } else if (document.caretRangeFromPoint) { const r = document.caretRangeFromPoint(x, y); if (r) [node, offset] = [r.startContainer, r.startOffset]; } if (!node || node.nodeType !== Node.TEXT_NODE || !sheet.contains(node)) return false; for (const i of [offset - 1, offset]) { if (i < 0 || i >= node.length || /\s/.test(node.data[i])) continue; range.setStart(node, i); range.setEnd(node, i + 1); for (const box of range.getClientRects()) if (x >= box.left && x <= box.right && y >= box.top && y <= box.bottom) return true; } return false; }; let x = 0, y = 0, queued = false, dousing = 0; const draw = () => { queued = false; const box = sheet.getBoundingClientRect(); sheet.style.setProperty("--cx", x - box.left + "px"); sheet.style.setProperty("--cy", y - box.top + "px"); if (overGlyph(x, y)) { clearTimeout(dousing); dousing = 0; sheet.classList.add("lit"); } else if (!dousing && sheet.classList.contains("lit")) { // A moment's grace, so it doesn't strobe across the spaces between words dousing = setTimeout(() => { sheet.classList.remove("lit"); dousing = 0; }, 350); } }; sheet.addEventListener("pointermove", (e) => { [x, y] = [e.clientX, e.clientY]; if (!queued) { queued = true; requestAnimationFrame(draw); } }); sheet.addEventListener("pointerleave", () => { clearTimeout(dousing); dousing = 0; sheet.classList.remove("lit"); });})();
I'm Michael Sendker, a software developer from The Other Bay Area (Tampa
Bay).
I find the pain points in how companies build and test their software,
and engineer them away. If you want to know more about me, check my
about page, or look through my
projects.
If you want to see about hiring me, and my one man developer agency
Standing Water LLC, please email me: m at standingwater dot io.
How this site is built
standingwater.io has had a few lives. The first two were Gatsby sites: one
started from an HTML5 UP template, then a playful, colorful version I
built from scratch.
In May 2022, my dog Sky died. The day she passed, I sat down to write
about her, and Gatsby's development server refused to start. I wrote the
post anyway, pushed it, and hoped Netlify would build it. It did, but I
was done fighting broken dependencies just to write. So I wrote my own
static site generator, modeled on the wonderfully simple
Sergey, and named it bluesky, after
her. I started it in C, switched to C++ after two weeks of struggling,
and by September 2022 this site was running on it.
I've been fascinated by parsers ever since I fumbled my way into
hand-writing them, with no idea what I was doing, to process texts for
anno.wiki. In 2023 I had an
idea for a language: C, plus the smallest possible version of classes.
Structs get methods, and . and -> mean the
same thing. I called it c′ (c-prime: the derivative of C, so its files
end in .dcdx).
I always meant to build it myself. I read Crafting Interpreters,
and bought Engineering a Compiler and the dragon book. I spent a
year wanting to learn compilers and performance properly, and yak shaved
my way into the math underneath it all with
cheap experiments
instead of just focusing on compilers. Then I got nerd sniped by epic
poetry and learning Latin, and the compiler books stayed on the shelf.
In 2026, after spending a lot of time using Claude Code for some new
personal experiments and projects, on a lark I asked Claude to try
building c′.
cprime, a c′
compiler forked from Rui Ueyama's
chibicc that now compiles
itself (the receiver ended up being called ego).
What a let down. Here's a Hello, World:
From there I thought, let's get it self-hosting! No sooner asked than
done. Then let's rewrite bluesky in c′! Et voilà :
skyblue, a rewrite of
bluesky in c′ 😞. (Jack Dorsey took bluesky out from under me! 🤬) No
matter, her name actually was Sky Blue Sendker. But once that was done,
all that was left was for it to rewrite standingwater.io in skyblue.
🫲 Atque ecce. 🫱
This is all very bittersweet. A mountain of effort in 15 minutes to get
the thing I dreamed of for 3–5 years. But we're all figuring out what
we're good for these days. You can see some of my new projects on the
projects page. AI assisted projects are clearly
marked.
So anyway, today this site is built by skyblue, which is compiled by
cprime, which bootstraps itself from C on every deploy to Netlify via
GitHub. Here's what's under this paragraph, from the top down. skyblue
pulled each excerpt out of the real source when it built this page, and
the button at the
top lifts the page off the files it was made from.
voidRender.slot(Node *node) {if (!ego.slots) { node.loc.warn("<sky-slot> only has a value in a template");return; }Slot *slot = ego.slots.get(node.name);if (slot) { slot.used = true;ego.out.push(slot.html); }}
skyblue fills the slot with this method. ego is the Render it's called on.
#!/bin/bash# Netlify runs this (see netlify.toml). skyblue is written in c′, so build# the compiler first; it bootstraps from C, so only cc and make are needed.set -euo pipefailgit submodule update --initmake -C cprimemake -C skyblue./skyblue/skyblue -i src -o build
Netlify runs this on every push: cc builds stage 0, stage 0 builds cprime, cprime builds skyblue, and skyblue builds this page.