<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Manu Martínez-Almeida — Writing</title><description>Essays, project write-ups, and experiments — compilers, graphics, infrastructure, and open source.</description><link>https://manualmeida.dev/</link><atom:link href="https://manualmeida.dev/rss.xml" rel="self" type="application/rss+xml"/><item><title>NoiseLang: Where N = 5 is a Dirac delta</title><link>https://manualmeida.dev/articles/noiselang/</link><guid isPermaLink="true">https://manualmeida.dev/articles/noiselang/</guid><description>A signals-and-noise course left me writing Monte Carlo math on paper and wishing the notation itself would run. Nine years later, with an AI agent doing the heavy lifting, NoiseLang became a real Monte Carlo language that runs the same fused kernel on the GPU, on every CPU core, and on WebAssembly.</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During &lt;a href=&quot;https://www.tel.uva.es/en/studies/degrees/itec.htm&quot;&gt;my telecommunications degree&lt;/a&gt; I took a
course on signals and noise, I spent a lot of evenings writing probability by hand: expectations,
variances, the odds of two random variables landing in some region. It always sucked, when I tried to run it on a computer, so much boilerplate.&lt;/p&gt;
&lt;p&gt;That wish became &lt;a href=&quot;https://noiselang.com&quot;&gt;NoiseLang&lt;/a&gt;. I started it about nine years ago, however, I never finished it.
Only recently, I brought it back thanks to AI tools and something far more ambitious than what I could have built alone
the first time.&lt;/p&gt;
&lt;h2&gt;Everything is a distribution&lt;/h2&gt;
&lt;p&gt;The whole language hangs on one idea, that &lt;strong&gt;every value is a probability distribution&lt;/strong&gt;. A plain number is
a Dirac spike, a distribution with all its weight on a single value. Since constants and random variables are the same kind of object, every operator
in the language maps distributions to distributions.&lt;/p&gt;
&lt;p&gt;Purists will tell you that the Dirac delta belongs to continuous densities and has no business on a
six-sided die, and they are right. What I mean is the Dirac measure, all the weight on one point,
which works fine on a die. Noise never evaluates a density anyway, it draws samples. And a constant
collapses back to a plain integer in the graph, so &lt;code&gt;5&lt;/code&gt; costs nothing.&lt;/p&gt;
&lt;p&gt;A name always refers to one fixed node, the same way &lt;code&gt;X&lt;/code&gt; is the same &lt;code&gt;X&lt;/code&gt; across a whole page of math.
So &lt;code&gt;X + X&lt;/code&gt; is &lt;code&gt;2X&lt;/code&gt; and &lt;code&gt;X - X&lt;/code&gt; is exactly &lt;code&gt;0&lt;/code&gt;. If you want variable independence you
write separate draws, ie, using &lt;code&gt;~&lt;/code&gt; multiple times, or &lt;code&gt;~[N]&lt;/code&gt; to draw &lt;code&gt;N&lt;/code&gt; independent variables into a vector.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X ~ unif_int(1, 6)
Y ~ unif_int(1, 6)
X + Y                 # two independent dice, a real 2d6 distribution
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing runs until you ask for some results, for example, &lt;code&gt;P(X + Y &amp;lt; 10)&lt;/code&gt;, at that moment it forces the runtime to
run millions of simulations (across all cores, if available) and return an estimate with a standard error attached.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Bday = unif_int(1, 365)
days ~[23] Bday          # 23 people in a room
P(has_duplicates(days))  # the birthday paradox, about 0.507
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is much easier to watch than to describe, so I built some cool demos!&lt;/p&gt;
&lt;h2&gt;Why it sat for nine years&lt;/h2&gt;
&lt;p&gt;The design was never the hard part, because a parser and a tree-walking interpreter for this language
is a weekend of work. The problem was everything else, writing an efficient Monte Carlo runtime, instead of a naive interpreter, conditional bayesian inference, and more.&lt;/p&gt;
&lt;p&gt;Current version is a compiler with a GPU backend (WGSL through &lt;a href=&quot;https://wgpu.rs/&quot;&gt;wgpu&lt;/a&gt;), a
&lt;a href=&quot;https://webassembly.org/&quot;&gt;WASM&lt;/a&gt; backend, and a pile of careful numerical code, so for a
cute-weekend project it stayed permanently out of reach.&lt;/p&gt;
&lt;h2&gt;Building the ambitious version with an agent&lt;/h2&gt;
&lt;p&gt;At my day job and side projects, I am experimenting with the boundaries of what today’s AI agents can do. For example, I am also porting a game I built 15 years ago for iOS, in archaic Objective-C to a modern game engine (with relative success).&lt;/p&gt;
&lt;p&gt;With NoiseLang, I realized, AI is great at building the JIT parts, the runtime parts, the numerical parts, but it sucks at coming up with good language design ideas, many times overriding existing language features for different purposes, or coming up with different syntax for non-orthogonal features.&lt;/p&gt;
&lt;h2&gt;One IR, three backends&lt;/h2&gt;
&lt;p&gt;The first plan was to compile the graph into WASM and let a WASM runtime JIT it for me, because
that runtime was surely going to optimize better than any compiler I could write myself. While
exploring that, I realized I could reach for the JIT optimizer directly and skip the WASM step, so
the first native backend was a &lt;a href=&quot;https://cranelift.dev/&quot;&gt;Cranelift&lt;/a&gt; JIT, a killer library for a
project like this, NoiseLang ran at native speed and I barely wrote any compiler. That JIT is gone
now. Keep reading, because the reason it left is my favorite part of the whole project.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~&lt;/code&gt; and the distribution constructors build an append-only DAG called the RvGraph. This graph is the single source of truth,
which is later converted into three different code paths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a columnar batch interpreter that works everywhere and acts as the correctness oracle;&lt;/li&gt;
&lt;li&gt;a WGSL emitter that fuses a whole expression into one GPU compute shader;&lt;/li&gt;
&lt;li&gt;a WASM emitter that does the same fused kernel for the browser’s CPU.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;flowchart LR
  src[&quot;X ~ unif(1, 6)&quot;] --&amp;gt; ir[&quot;RvGraph&quot;]
  ir --&amp;gt; interp[&quot;batch interpreter&quot;]
  ir --&amp;gt; wgsl[&quot;WGSL emitter (GPU)&quot;]
  ir --&amp;gt; wasm[&quot;WASM emitter&quot;]
  wgsl -. fallback .-&amp;gt; interp
  wasm -. fallback .-&amp;gt; interp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One shared module defines what the graph means, so the two code generators stay thin and cannot drift
apart. Anything a backend can’t successfully compile falls back to the interpreter, and the results stay
identical across backends and core counts. All tests run in all three code paths and compared to be bit-identical.&lt;/p&gt;
&lt;p&gt;The engine picks the path per query, the same way a SQL query planner picks an execution plan: a
cost model keeps the small queries on the vectorized interpreter, fans the medium ones out across
every core with a work-stealing reducer, and sends the heavy ones to the GPU as one fused compute
shader. You write &lt;code&gt;P(...)&lt;/code&gt;, the planner does the rest.&lt;/p&gt;
&lt;h2&gt;Making the Monte Carlo loop cheap&lt;/h2&gt;
&lt;p&gt;All the performance work is about one loop: draw a few million samples, evaluate the expression on
each, and reduce the results. A handful of techniques carry most of it, while keeping the results deterministic (that was the hard part).&lt;/p&gt;
&lt;p&gt;Kernel fusion keeps every intermediate value in registers, so an arithmetic-heavy expression stays in registers. The PRNG compiles into the
kernel, and the &lt;code&gt;ln&lt;/code&gt;, &lt;code&gt;sin&lt;/code&gt;, and &lt;code&gt;cos&lt;/code&gt; become inline polynomial
approximations, speeding up the kernel by a factor of 2.&lt;/p&gt;
&lt;p&gt;My favorite trick used to be in the RNG. The original generator
(&lt;a href=&quot;https://prng.di.unimi.it/&quot;&gt;xoshiro256++&lt;/a&gt;) is a serial dependency chain, so instead of fighting
that, the kernel ran four independent streams at once and let the out-of-order core overlap them.
That trick is retired now, and the reason is better than the trick: the RNG became counter-based
(&lt;a href=&quot;https://arxiv.org/abs/2004.06278&quot;&gt;squares64&lt;/a&gt;), a middle-square hash where every draw is a pure
function of (seed, lane, source). No state to thread through the loop means any slice of the sample
space can run on any core, or any GPU thread, and land bit-identical. And it is not a random pick
(pun intended), the exact bit stream the engine consumes passed 1 TB of
&lt;a href=&quot;https://pracrand.sourceforge.net/&quot;&gt;PractRand&lt;/a&gt; with zero anomalies.&lt;/p&gt;
&lt;p&gt;On my 14-core M4 Pro, a one-line &lt;code&gt;P(...)&lt;/code&gt; sustains around 5.8 billion samples per second and scales
about 9.6× from one core to all of them. Per core, the JIT kernel ran within about 1.15× of
hand-written Rust compiled by LLVM, which told me the CPU codegen had hit its ceiling. The same
fused loop, emitted as WASM, runs at roughly half to three-quarters of native speed inside V8.&lt;/p&gt;
&lt;h2&gt;The GPU ate the JIT&lt;/h2&gt;
&lt;p&gt;A few weeks after I published this article, I deleted the JIT. The idea clicked when I noticed
that a Monte Carlo query is already shaped like a compute shader: one pure kernel evaluated over
millions of independent lanes, then a reduce to fold them down. That is exactly the GPU compute
model, so the RvGraph got a WGSL lowering, and thanks to &lt;a href=&quot;https://wgpu.rs/&quot;&gt;wgpu&lt;/a&gt; the same emitter
reaches Metal, Vulkan and DirectX 12 on native, and WebGPU in the browser. One GPU backend to
maintain, instead of one per platform.&lt;/p&gt;
&lt;p&gt;Then I measured the full example corpus on the M4 Pro:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;backend&lt;/th&gt;
&lt;th&gt;corpus total&lt;/th&gt;
&lt;th&gt;speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;interpreter&lt;/td&gt;
&lt;td&gt;3854 ms&lt;/td&gt;
&lt;td&gt;1.00×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cranelift JIT&lt;/td&gt;
&lt;td&gt;3274 ms&lt;/td&gt;
&lt;td&gt;1.18×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;923 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.17×&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JIT + GPU&lt;/td&gt;
&lt;td&gt;843 ms&lt;/td&gt;
&lt;td&gt;4.57×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Once the GPU exists, everything the JIT adds is 80 ms, and it only ever worked on native. So it
went, 1,834 lines and four dependencies deleted, and the shipped CLI jumped from the interpreter
to the GPU by default. Deleting a backend was the fastest release I ever cut!&lt;/p&gt;
&lt;p&gt;The heavy examples are where it gets fun. A quantization example that draws random rotation matrices
went from 1474 ms to 55 ms (26×). The 100-prisoners simulation needed real compiler work first:
by the time the emitter sees the graph, a &lt;code&gt;for&lt;/code&gt; loop is unrolled into ~15,000 data-dependent
reads, and the Metal shader compiler takes 2.2 seconds to chew on that. The fix was to capture the
loop as a single node at eval time and emit it back as an actual WGSL loop, and the simulation
dropped from 59 ms to 2.8 ms.&lt;/p&gt;
&lt;h2&gt;Where Noise sits&lt;/h2&gt;
&lt;p&gt;NoiseLang is a toy language, &lt;strong&gt;you probably should not use it for anything serious&lt;/strong&gt;, however I wish this language existed during my university days.&lt;/p&gt;
&lt;p&gt;For a language nerd, it’s a small expression-based language over a static random-variable algebra,
with forward Monte Carlo and rejection-based conditioning.&lt;/p&gt;
&lt;p&gt;You might ask, how does it compare to NumPy or Stan? NumPy makes you write the simulation yourself, and Stan makes you declare a model and wait for a
sampler. Noise lets you write the probability as math while running Monte Carlo under the hood to get the answer.&lt;/p&gt;
&lt;p&gt;Stan and PyMC beat Noise at the thing they’re built for, fitting a posterior to lots of continuous
data with their HMC/NUTS samplers, and NumPy beats it at raw array crunching. Conditioning in Noise is
rejection-based, so it works great for a handful of discrete observations but becomes useless for ten
thousand continuous measurements, and there is no stateful simulation yet (no Markov chains yet).
Noise wins when you have a probability question and you wanna know the answer without much hassle.&lt;/p&gt;
&lt;p&gt;So use Noise for the whiteboard stage of a problem, when you want to run the math you just wrote, and
move to Stan or PyMC when you need a real posterior, or to NumPy and JAX when you need to go to production.&lt;/p&gt;
&lt;h2&gt;Back to signals and noise&lt;/h2&gt;
&lt;p&gt;Going back to my university days, there was this subject called “Señales Aleatorias Y Ruido”, which is a spanish translation of “Random Signals and Noise”.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/noiselang/senales-aleatorias-y-ruido.jpg&quot; alt=&quot;Cover of the textbook Señales Aleatorias y Ruido&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    The textbook, in all its glory.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This subject was the nightmare of many students, including myself, in fact, I failed it. Truth be told, I didn’t put enough effort into it during the first year, but it changed when I had the take the same subject again in the second year.
The professor was great, and made the subject interesting. The things that blew my mind was how he could model why FM survives a noisy channel when AM doesn’t.&lt;/p&gt;
&lt;p&gt;So, here is my tribute to the subject, a one-screen Noise program that models why FM survives a noisy channel when AM doesn’t.&lt;/p&gt;
&lt;p&gt;One caveat, and &lt;code&gt;roger_&lt;/code&gt; on Hacker News caught it before I did. The program above modulates phase, not
frequency, so technically it is PM. For the single tone I am sending, PM and FM are the same thing up
to a scale factor and a shift, so the demo still shows what I want it to show, but real FM would
differentiate the message before it goes into the phase. Now I want to write that version, because FM
should pull even further ahead once the message stops being a single tone.&lt;/p&gt;
&lt;h2&gt;Run NoiseLang in the browser&lt;/h2&gt;
&lt;p&gt;Everything above runs on &lt;a href=&quot;https://www.npmjs.com/package/@noiselang/core&quot;&gt;&lt;code&gt;@noiselang/core&lt;/code&gt;&lt;/a&gt;, thanks to the Rust engine compiled to WebAssembly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install @noiselang/core
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;

const result = await run(`
  X ~ rand::unif(-1, 1);
  Y ~ rand::unif(-1, 1);
  4 * P(X^2 + Y^2 &amp;lt; 1)
`);

console.log(result.value); // &quot;3.1415…&quot; — the last statement&apos;s value
console.log(result.output); // everything Print(...) emitted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;run&lt;/code&gt; never throws, failures come back on &lt;code&gt;result.error&lt;/code&gt; with a source span. There is also
&lt;code&gt;runWithIntrospection&lt;/code&gt;, the API behind the variable inspector at &lt;a href=&quot;https://noiselang.com&quot;&gt;noiselang.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And when the page has WebGPU available, the heavy queries run on your GPU with the same WGSL
shaders as native. Everything else stays on the WASM kernel, so nothing breaks on browsers that
don’t have it.&lt;/p&gt;
&lt;p&gt;NoiseLang is playable in the browser at &lt;a href=&quot;https://noiselang.com&quot;&gt;noiselang.com&lt;/a&gt;. Open it, type
&lt;code&gt;X ~ unif(-1, 1); Y ~ unif(-1, 1); 4 * P(X^2 + Y^2 &amp;lt; 1)&lt;/code&gt;, and watch a few million
draws estimate π from your browser tab.&lt;/p&gt;
&lt;h2&gt;Appendix: prior art&lt;/h2&gt;
&lt;p&gt;I posted this on &lt;a href=&quot;https://news.ycombinator.com/item?id=48803791&quot;&gt;Hacker News&lt;/a&gt; and the thread turned
into a reading list, which is the best thing that can happen to you when you ship a toy language.
NoiseLang is not the first language to treat probability as a first-class value, not even close, so
here is what people pointed me to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://monad-bayes.netlify.app/&quot;&gt;monad-bayes&lt;/a&gt;, a Haskell library where the same “everything is a distribution” idea falls out of the monad, courtesy of &lt;code&gt;bradrn&lt;/code&gt;. I had no idea Haskell could express this so directly.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.webppl.org/&quot;&gt;webppl&lt;/a&gt;, &lt;a href=&quot;https://probprog.github.io/anglican/&quot;&gt;Anglican&lt;/a&gt;, &lt;a href=&quot;https://mc-stan.org&quot;&gt;Stan&lt;/a&gt; and &lt;a href=&quot;https://www.pymc.io/&quot;&gt;PyMC&lt;/a&gt;, the real probabilistic programming languages, suggested by &lt;code&gt;chrisra&lt;/code&gt;. If you have an actual model to fit, go there, not here.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://diceplots.com&quot;&gt;diceplots.com&lt;/a&gt; by &lt;code&gt;qdotme&lt;/code&gt;, which keeps the distributions exactly analytical at every step and never samples. The opposite trade-off to mine, and a fun one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/pdf/1006.0764&quot;&gt;“General Purpose Convolution Algorithm in S4-Classes by means of FFT”&lt;/a&gt; by Ruckdeschel and Kohl, sent by &lt;code&gt;data-ottawa&lt;/code&gt;. It is the paper behind the R package &lt;code&gt;distr&lt;/code&gt;, and it overloads &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt; on distributions by computing the convolutions with an FFT instead of sampling them. Exactly the operator algebra I wanted, arrived at from the other direction.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kccqzy&lt;/code&gt;’s &lt;a href=&quot;https://github.com/kccqzy/probabilistic-program-inference&quot;&gt;work on probabilistic program inference&lt;/a&gt;, which handles loops without Monte Carlo at all. He also asked the question I was hoping nobody would ask, which is how NoiseLang handles loops, and the answer is that it does not.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Big thanks also to &lt;code&gt;RossBencina&lt;/code&gt; and &lt;code&gt;thrtythreeforty&lt;/code&gt;, who pushed on the RNG trick until I understood
my own benchmark better, and to &lt;code&gt;roger_&lt;/code&gt;, who noticed that my FM demo is really doing phase modulation.
This is why you post your toys!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://manualmeida.dev/articles/noiselang&quot;&gt;This article has interactive figures — read it on manualmeida.dev.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Systems</category><category>programming languages</category><category>probability</category><category>compilers</category><category>side project</category></item><item><title>Qwik: Resumability That Feels Like React</title><link>https://manualmeida.dev/articles/qwik-resumability/</link><guid isPermaLink="true">https://manualmeida.dev/articles/qwik-resumability/</guid><description>I joined Miško Hevery&apos;s team and helped turn Qwik from a resumable-but-painful prototype into a framework that feels like React, backed by a Rust compiler that extracts closures and serializes app state into HTML, so the fast path is the default one.</description><pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Performance is a human problem, not a technology one&lt;/h2&gt;
&lt;p&gt;The thing I came to believe working on Qwik is that the web’s performance problem isn’t a
technology problem. It’s a design and developer-experience problem. Developers, like water, follow the
path of least resistance, and in most frameworks the easy way to build something is also the way that
ships a slow site. We then tell people to go optimize it afterward, as a chore, trading off
capabilities or DX or their weekend to claw the performance back.&lt;/p&gt;
&lt;p&gt;I find it more honest to stop blaming developers and fix the path instead. A framework should be
designed so the path of least resistance lands you on a fast site by default. You shouldn’t have to be
a performance expert to ship a fast page; you should have to work to ship a slow one.&lt;/p&gt;
&lt;p&gt;And the thing worth optimizing is almost always JavaScript. Most sites already handle their images and
CSS reasonably well, so there’s little left on the table there. JavaScript is where the big wins hide.
The difference between a sluggish page and a snappy one is often tens of points of Lighthouse score, and
for anyone running an e-commerce or consumer site, that gap is revenue. Resumability is the lever Qwik
pulls on exactly that.&lt;/p&gt;
&lt;h2&gt;The idea: resume, don’t rebuild&lt;/h2&gt;
&lt;p&gt;Every mainstream framework hydrates. The server renders HTML, then the browser downloads the whole
component tree as JavaScript, re-executes it, and reattaches event listeners to reconstruct state the
server already had. You pay, in bytes and CPU, to rebuild something you were handed seconds ago.&lt;/p&gt;
&lt;p&gt;Islands and partial hydration, the approach Astro popularized, make this better by only hydrating
the interactive bits. But each island still has to download and execute its JavaScript before it’s
ready, and someone has to draw and maintain those island boundaries by hand.&lt;/p&gt;
&lt;p&gt;Resumability skips the rebuild entirely. The server serializes the application’s state and its event
wiring into the HTML itself. The browser ships almost no JavaScript up front. Conceptually the app
becomes a hashmap: an event on an element points at the one chunk of code that handles it. When you
click a button, Qwik reads an attribute on that element, fetches that small chunk, restores the state
it captured, and runs it. There’s no global “boot the app” step, because there’s no app to boot.&lt;/p&gt;
&lt;p&gt;There’s a neat way to see the flip. In a hydrated app, event handlers are the &lt;em&gt;last&lt;/em&gt; thing to become
ready, after the whole tree has downloaded and executed. In a resumable app they’re the &lt;em&gt;first&lt;/em&gt;. The
page is interactive the moment it arrives.&lt;/p&gt;
&lt;h2&gt;The first version worked; writing it was the hard part&lt;/h2&gt;
&lt;p&gt;Proving resumability was one thing. The developer experience was another. In &lt;a href=&quot;https://github.com/QwikDev/qwik/tree/a64a648e6b12ec5acd1e55decd734cb3b474b7af/integration/todo/ui/Header&quot;&gt;that early version&lt;/a&gt;
the philosophy leaked all the way into the API. You split your code into many small files by hand, you
referenced lazy-loadable symbols by name, and you wrote a lot of ceremony to tell the runtime how the
pieces fit together. It worked, and it asked too much of the person typing.&lt;/p&gt;
&lt;p&gt;My bet was that all of that ceremony could become the compiler’s job. A developer should write
something that looks like ordinary &lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;, with components, event handlers, and local state, and the
build step should do the hard work of making it resumable. The point of the whole project, after all,
was to make the fast path the easy path, and an API that demanded this much manual wiring was the
opposite of that.&lt;/p&gt;
&lt;h2&gt;Making it feel like React&lt;/h2&gt;
&lt;p&gt;In the design I led, you write a component with handlers and hooks, the way you already think. The
magic lives in the compiler.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const Counter = component$(() =&amp;gt; {
  const count = useSignal(0);
  // looks like an ordinary closure; the compiler will extract it
  return &amp;lt;button onClick$={() =&amp;gt; count.value++}&amp;gt;{count.value}&amp;lt;/button&amp;gt;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler reads the AST, finds every &lt;code&gt;$&lt;/code&gt;-marked boundary (a &lt;code&gt;component$&lt;/code&gt;, an &lt;code&gt;onClick$&lt;/code&gt;), and
pulls that closure out into its own module with no dependencies except the variables it closed over.
Those captured variables get serialized straight into the HTML. When the user clicks, the runtime
loads that one module, restores the state it captured, and runs the handler. Nothing else downloads.&lt;/p&gt;
&lt;p&gt;The captured state can be a live signal, so the handler reads and updates a reactive &lt;code&gt;count&lt;/code&gt; and the
DOM follows along. &lt;code&gt;useSignal&lt;/code&gt; lets the runtime make a surgical update to exactly the text node that
changed, with no component re-render in the browser. The signal model drew on Solid’s
&lt;a href=&quot;https://www.solidjs.com/&quot;&gt;reactivity&lt;/a&gt;. The result reads like React and behaves like a resumable app,
with none of the manual wiring the first version demanded.&lt;/p&gt;
&lt;p&gt;One thing I like about this model over React Server Components: a Qwik component is universal. The same
component runs on the server or the client, and the developer doesn’t annotate the boundary. With RSC
it’s easy to reach for &lt;code&gt;use client&lt;/code&gt; everywhere out of convenience and quietly opt back into shipping
everything. In Qwik there’s no boundary to get wrong, because the compiler is the one deciding what
crosses it.&lt;/p&gt;
&lt;h2&gt;Rust and SWC&lt;/h2&gt;
&lt;p&gt;The optimizer that does all this is written in Rust, on top of &lt;a href=&quot;https://swc.rs/&quot;&gt;SWC&lt;/a&gt;. It runs on
every build and across large codebases, so it had to stay fast enough to disappear into the dev loop.
Rust kept the AST passes quick as projects grew, which matters when the entire premise is that the
compiler, and not the developer, carries the complexity.&lt;/p&gt;
&lt;h2&gt;QwikCity&lt;/h2&gt;
&lt;p&gt;Later, with Adam Bradley (co-creator of Ionic and Stencil), I worked on the design of QwikCity, the
meta-framework that sits on top: routing, data loading, and the conventions that make Qwik a way to
build whole sites. That part was a close back-and-forth between the two of us.&lt;/p&gt;
&lt;h2&gt;Credit where it’s due&lt;/h2&gt;
&lt;p&gt;I want to be precise about this. The resumability concept, and much of how it works under the hood,
including the runtime and the Solid-inspired signals, came from Miško Hevery, the creator of
&lt;a href=&quot;https://angular.dev/&quot;&gt;Angular&lt;/a&gt;.
What I led was the developer-facing redesign and the compiler beneath it: turning closures into
independent, serializable modules and working out the reachability analysis that decides what to ship.&lt;/p&gt;
&lt;p&gt;The throughline to the rest of my work is that compiler. Make the source look ordinary, push the hard
analysis to build time, and let the runtime stay lazy, so the path of least resistance is also the
fast one. I’d wired the same instinct into
&lt;a href=&quot;https://manualmeida.dev/articles/gin-simple-over-easy&quot;&gt;Gin’s router&lt;/a&gt; years earlier, and I’d reach for it again tomorrow.
If you’re designing a framework, that’s where I’d spend the effort: on the compiler that lets people
write the easy thing while you quietly ship the simple one.&lt;/p&gt;
&lt;p&gt;If you want the longer version of this argument, I gave a
&lt;a href=&quot;https://www.youtube.com/watch?v=HfEDhuZKH7A&quot;&gt;talk on it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://manualmeida.dev/articles/qwik-resumability&quot;&gt;This article has interactive figures — read it on manualmeida.dev.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Open Source</category><category>qwik</category><category>compilers</category><category>rust</category><category>performance</category><category>web</category></item><item><title>Releasing OpenView</title><link>https://manualmeida.dev/articles/openview-health/</link><guid isPermaLink="true">https://manualmeida.dev/articles/openview-health/</guid><description>OpenView Health is a free app anyone can use to visualize and securely share their medical data. A modern DICOM visualizer powered by set.health.</description><pubDate>Sat, 02 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Originally published on the set.health blog, April 2, 2022.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Today we are happy to announce the &lt;a href=&quot;https://web.archive.org/web/20220405133822/https://openview.set.health/&quot;&gt;OpenView initiative&lt;/a&gt;,
a &lt;strong&gt;non-profit project&lt;/strong&gt; that deeply connects with our vision of global healthcare.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/openview-health/openview.png&quot; alt=&quot;Screenshot of Open View health&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 1.&lt;/span&gt; Screenshot of Open View health.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let’s stop for a minute and think! Imagine you had a ski accident. Now you can’t stand up, there’s an
intense pain in your knee. You are worried about your leg, generally you like doing sports. In the local
hospital your leg was examined in an MRI and you have to face the fact that you probably need to go
through surgery because your ACL (anterior cruciate ligament) is injured, and your knee keeps swallowing.
You are trying very hard to imagine what is going on in your leg and want to ask another doctor to give
you a secondary opinion. Let’s say you have a CD or a stick with your MRI images in hands and you visit a
different orthopedist. He says his software is not able to open your image, so you two need to find
another way how to open the images. Very complicated and time consuming. So many steps until the
physician gets to the point to check your medical data.&lt;/p&gt;
&lt;p&gt;Scenario 2: your MRI images are in a fully anonymized link, you can securely share with anyone. You can
easily ask for a secondary opinion about your condition. Additionally you are able to see it in 3D so
everything is clear not only to the doctor, but to you as well. Less fear, faster recovery.&lt;/p&gt;
&lt;p&gt;OpenView was built for 3 reasons and I’m going to lead you through these crucial points.&lt;/p&gt;
&lt;h2&gt;I. User friendly technology&lt;/h2&gt;
&lt;p&gt;Having surgery no matter what, is a big deal. I think we all can agree on that, right?! We built the app
with privacy in mind. We know, not everyone is a hardtech genius, so we kept it as simple as possible.
People need a diagnosis. They have the right to access their medical data and to share it with their
loved ones or to ask for a secondary opinion.&lt;/p&gt;
&lt;h2&gt;II. Fully anonymized&lt;/h2&gt;
&lt;p&gt;We built this app with privacy in mind. We know, most people have unanswered questions and doubts about
the web. So let me write it out “loud”: it cannot be safer!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is an open source project&lt;/li&gt;
&lt;li&gt;We don’t track any users&lt;/li&gt;
&lt;li&gt;All data is encrypted end-to-end&lt;/li&gt;
&lt;li&gt;Added data is anonymized following HIPAA and GDPR guidelines&lt;/li&gt;
&lt;li&gt;All data and processing remains offline, until the user decides to share it&lt;/li&gt;
&lt;li&gt;Decentralised storage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;III. Decentralised and non-profit&lt;/h2&gt;
&lt;p&gt;OpenView will never make a business out of patients’ data and we took extraordinary measure to ensure
that. The data is stored using &lt;a href=&quot;https://ipfs.io/&quot;&gt;IPFS (Interplanetary Filesystem)&lt;/a&gt;, a distributed
network of storage servers owned by individuals, universities and companies. IPFS supports other
projects such as the WebArchive, and massive datasets of medical images. We are looking for partnerships
with universities and institutions that are willing to donate storage resources to power the network.
Then, medical datasets will be publically available for anyone instered from big companies to students
for free, 100% public domain.&lt;/p&gt;
&lt;p&gt;OpenView is powered by set.health Core, our commercial product to help companies to build better
healthcare products. Our business model is licensing our API for developers and consulting, not selling
data. Additionally you can donate your own data.&lt;/p&gt;
&lt;h2&gt;Do you have an app and want to boost it?&lt;/h2&gt;
&lt;p&gt;Reach out to us!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend solutions&lt;/li&gt;
&lt;li&gt;First class support&lt;/li&gt;
&lt;li&gt;We are experts of custom medical imaging and DICOM solutions&lt;/li&gt;
&lt;li&gt;Our code has 0 dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Do you want to be part of the project?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If you are a developer, feel free to open an issue or a &lt;a href=&quot;https://github.com/sethealth/openviewhealth&quot;&gt;pull request in Github&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you are a patient. Just feel free to use the app, and donate your data anonymously.&lt;/li&gt;
&lt;li&gt;If you are a university, we want to heard from you! We need your help to connect the data into actual
research and progress. Please drop us an email to team@set.health.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20220405133822/https://openview.set.health/&quot;&gt;Open OpenView app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;OpenView and set.health are no longer online, and team@set.health no longer reaches anyone — the links
above go to how the app looked the week this was published. The source is still on
&lt;a href=&quot;https://github.com/sethealth/openviewhealth&quot;&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Open Source</category><category>medical imaging</category><category>dicom</category><category>privacy</category><category>open source</category><category>ipfs</category></item><item><title>How Breaking My Leg Gave Me Time to Stop and Think</title><link>https://manualmeida.dev/articles/sethealth-gpu-ct/</link><guid isPermaLink="true">https://manualmeida.dev/articles/sethealth-gpu-ct/</guid><description>How I broke my ankle and built set.health, a platform to streamline medical data into amazing healthcare products for patients, doctors and researchers.</description><pubDate>Wed, 20 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today I’m incredibly excited to show off a project I’ve been working on, but first a quick back story.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/ray-tracing.jpg&quot; alt=&quot;Ray tracing of my ankle using set.health tech&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 1.&lt;/span&gt; Ray tracing of my ankle using
    set.health tech. The loose fragments along the right are the displaced
    pieces of the break.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I have been writing software since I was 10 years old. Back then, I barely knew what I was doing,
gluing together parts of different videogame websites I loved.&lt;/p&gt;
&lt;p&gt;I do remember though the first moment my brain clicked and realized that &lt;strong&gt;programming was more than
putting things together&lt;/strong&gt;, but gives you the opportunity to build new things from the ground up. Since
then, it was all about the product, building new things.&lt;/p&gt;
&lt;p&gt;When the first iPhone was released, I was fascinated with the idea of building something that could be
used by thousands of people. So I joined an open-source &lt;a href=&quot;https://github.com/cocos2d/cocos2d-objc&quot;&gt;project to build videogames&lt;/a&gt;.
I learned a lot! Computer graphics, teamwork, and how a game engine works under the hood, someone could
say I got my CS “degree” during that time.&lt;/p&gt;
&lt;p&gt;This allowed me to build and ship a couple of games, one of them &lt;a href=&quot;https://www.metacritic.com/game/ios/infinity-field&quot;&gt;very successful&lt;/a&gt;,
I worked tirelessly polishing the gameplay, the design of every little aspect, adjusting the difficulty
and the controls obsessively. &lt;strong&gt;It was all about the product&lt;/strong&gt;, I didn’t care much about the code, it
was a mere tool to accomplish something, but then I fell in love.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I fell in love with software engineering&lt;/strong&gt;, I rediscovered the complexity of building software, the
software itself. The developer experience of people using my code, how an API will be able to scale and
be future proof, optimizing processes so more developers can work without stepping on each other. Since
then, I have worked building SDKs and APIs, created &lt;a href=&quot;https://manualmeida.dev/articles/gin-simple-over-easy&quot;&gt;Gin&lt;/a&gt;, and Ionic and
&lt;a href=&quot;https://stenciljs.com/&quot;&gt;Stencil&lt;/a&gt; using web technologies.&lt;/p&gt;
&lt;p&gt;During all this time &lt;strong&gt;I had no time to stop and think&lt;/strong&gt;, not a single week without coding, week after
week, then year after year. Thinking more about the “Why”, rather than the “How”. However, back in
October 2019 something happened. One night some friends and I went out for drinks close to my office in
&lt;a href=&quot;https://www.google.com/maps/place/Kreuzberg,+Berlin&quot;&gt;Kreuzberg&lt;/a&gt;, a vibrant district of Berlin.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/friends.jpg&quot; alt=&quot;My friends in Berlin&quot; /&gt;
  &lt;/div&gt;
&lt;/figure&gt;
&lt;hr /&gt;
&lt;hr /&gt;
&lt;hr /&gt;
&lt;p&gt;That night I broke my ankle.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/fracture1.jpg&quot; alt=&quot;Picture of my ankle&quot; /&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/fracture2.jpg&quot; alt=&quot;Sagittal radiography of my ankle&quot; /&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/fracture3.jpg&quot; alt=&quot;Coronal radiography of my ankle&quot; /&gt;
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Suddenly, everything I was doing, I was working on stopped abruptly. For 14 days I “lived” in that
hospital of Kreuzberg, waiting for a surgery until the swelling shrinks. When I was able to calm down,
it was not that bad, I found time to read those books I always wanted to, and books I would have never
read, reconnected with people, wrote more, designed a tattoo for my ankle and even learned some German:
“Ich möchte gerne einen Kaffee mit Milch ohne Zucker”. &lt;strong&gt;A time to think and disconnect the autopilot.&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/tattoo.jpg&quot; alt=&quot;My tattoo&quot; /&gt;
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The worst was to come, even though the doctors assured the surgery went well, I had the feeling
something was not good. &lt;strong&gt;After dealing with a very stressful process, I managed to get my health data
onto a CD&lt;/strong&gt;, bought an external CD reader on Amazon and asked for a second opinion.&lt;/p&gt;
&lt;p&gt;I was told to come back immediately to Spain (where my family is from). Indeed, &lt;strong&gt;there were
problems&lt;/strong&gt;, the articular space was not respected, nerve damage because of the suture, an unrepaired
tendon and the syndesmosis screw not placed in the optimal position. I was scared, tired, and
depressed, but after the second surgery I was able to find some peace again.&lt;/p&gt;
&lt;p&gt;Without deadlines, I opened my laptop and &lt;strong&gt;remembered those old days where I used to write game
engines and build things&lt;/strong&gt;. After some research, I tried to visualize my health data, writing all the
software from scratch.&lt;/p&gt;
&lt;p&gt;It was a lot of fun, but how could I &lt;a href=&quot;https://www.youtube.com/watch?v=UF8uR6Z6KLc&quot;&gt;connect the dots&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;I decided it was time to think again in terms of product, something I would love to build and feel
passionate about.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/screenshot3.png&quot; alt=&quot;Tech demo showcasing my very own ankle&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 2.&lt;/span&gt; Tech demo showcasing my very own ankle.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Rendering gigabytes of voxels in a browser&lt;/h2&gt;
&lt;p&gt;A CT scan is a 3D grid of density samples, often hundreds of slices deep, which adds up to gigabytes.
You can’t download all of it before drawing anything, and you can’t fit all of it in GPU memory. Yet a
radiologist expects to spin and re-window the volume with no lag.&lt;/p&gt;
&lt;p&gt;The rendering itself is ray casting through a 3D texture. For each pixel on screen, march a ray into
the volume, sample density along the way, map each sample to color and opacity, and composite front to
back until the pixel goes opaque.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// simplified: march a ray through the CT volume and accumulate color + opacity
vec4 raymarch(vec3 origin, vec3 dir) {
  vec4 acc = vec4(0.0);
  for (int i = 0; i &amp;lt; STEPS; i++) {
    vec3 p = origin + dir * (float(i) * stepSize);
    float density = texture(volume, p).r;    // sample one voxel
    vec4 s = transfer(density);              // density -&amp;gt; color + alpha
    acc.rgb += (1.0 - acc.a) * s.a * s.rgb;  // front-to-back compositing
    acc.a   += (1.0 - acc.a) * s.a;
    if (acc.a &amp;gt; 0.99) break;                 // early ray termination
  }
  return acc;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three decisions made it run in real time on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt;.
We split each volume into smaller blocks, so the GPU only ever held the parts in view and could skip
the empty space between them. We quantized the data, including the normal vectors used for shading, to
shrink what had to live in texture memory. And we loaded progressively, coarse first and detail as you
leaned in, so the first frame appeared fast even on a multi-gigabyte study.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/openview.png&quot; alt=&quot;OpenView Health, a browser viewer showing a brain MRI in four synchronized panes: axial, coronal, sagittal slices and a 3D angiography render, with measurements and metadata&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 3.&lt;/span&gt; OpenView Health, the free viewer we
    built on the set.health SDK. Slices and the 3D render stay in sync, and you
    can measure, window, and annotate straight in the browser.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Keeping the data safe by never seeing it&lt;/h2&gt;
&lt;p&gt;Medical data raises the stakes on security, so we ran end-to-end encryption on the client. A scan never
left the device in the clear, which meant we couldn’t read it, and that was the point. The data we
never held unencrypted is data we could never leak. That one decision collapsed a large amount of
compliance and liability surface into something we could reason about.&lt;/p&gt;
&lt;h2&gt;Announcing set.health&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Healthcare is not a commodity&lt;/strong&gt;, at least it’s not for me, so building a health product requires a
strong ethical filter to discard bad ideas. Once you realize that &lt;strong&gt;patients are the true owners of the
data&lt;/strong&gt;, but still they can’t access it easily, you can see there is a problem. People need a diagnosis
to feel safe and well-informed, to be able to access and share their data and even having access to a
Wifi hotspot is critically important so &lt;strong&gt;they can connect with their loved ones in the emergency
room&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Today I am announcing &lt;a href=&quot;https://web.archive.org/web/20200522164645/https://set.health/&quot;&gt;set.health&lt;/a&gt;&lt;/strong&gt;, my bottom-up approach to solve this problem
by helping companies build better software and health products: patient apps, custom implant companies
to ship faster, health platforms to communicate better, &lt;a href=&quot;https://techcrunch.com/2020/01/10/medical-images-exposed-pacs/&quot;&gt;securing the medical data&lt;/a&gt;,
funneling anonymized medical data into &lt;a href=&quot;https://youtu.be/Rp7qqjlBeRY?t=4277&quot;&gt;truly open datasets&lt;/a&gt;… and
ultimately serve as a product for patients to understand and share their medical data.&lt;/p&gt;
&lt;p&gt;There are quite a few companies that also try to solve this problem under different approaches, and I
love it!&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/screenshot5.png&quot; alt=&quot;Screenshot of a COVID19 case, showing pneumonia&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 4.&lt;/span&gt; Technical demo of a COVID19 case using
    set.health directly from a browser.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;What it became&lt;/h2&gt;
&lt;p&gt;set.health shipped as an SDK and a compliant backend across iOS, Android, and the web, and it ran until 2026. The use case that clicked was custom prosthetics. A doctor could shape an implant against a
patient’s real anatomy, an engineer could validate it, and the two could hold a precise conversation
inside the same 3D view, all running on our SDK.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/custom-implants.png&quot; alt=&quot;A custom-implant ordering tool built on the set.health SDK, showing a fractured ankle with surgical plates and screws planned over the CT scan, alongside controls to order implants, surgical guides, and 3D-printed bio-models&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;
    &lt;span&gt;Figure 5.&lt;/span&gt; A custom-implant planning and ordering
    tool a partner built on the SDK: plan plates, screws, and surgical guides
    against a real fracture, leave notes, and order the printed parts. The tool
    is no longer online.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;2020 is being a complicated year for all of us, but &lt;em&gt;It Gets Worse Before It Gets Better&lt;/em&gt;. Today, I can
say that breaking my ankle might be one of the 5 best things that happened in my life, just time will
say in which position exactly.&lt;/p&gt;
&lt;p&gt;Please &lt;a href=&quot;https://web.archive.org/web/20220427060057/https://set.health/&quot;&gt;take a look&lt;/a&gt;, and send me an
&lt;a href=&quot;mailto:manu@manualmeida.dev&quot;&gt;email&lt;/a&gt; if you wanna know more, I would love to heard from you!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;set.health is no longer online — those links go to how the site looked the week I published this.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tschüss&lt;/strong&gt; 👋&lt;/p&gt;
</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Graphics</category><category>gpu</category><category>webgl</category><category>graphics</category><category>medical imaging</category><category>performance</category></item><item><title>Building Gin: Simple Over Easy</title><link>https://manualmeida.dev/articles/gin-simple-over-easy/</link><guid isPermaLink="true">https://manualmeida.dev/articles/gin-simple-over-easy/</guid><description>A stalled startup in 2014 became Gin: a Go web framework shaped by simple-over-easy design, a radix-tree router, and an API that still works ten years later.</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2014 I came back from San Francisco with no plan. I spent a year building SDKs at Joypad and
TinySpark after shipping one of my first games, and that year gave me a good sense of what small
software teams need from their tools. Back in Spain, about to start Telecommunications Engineering,
I had to decide what to build next.&lt;/p&gt;
&lt;p&gt;The answer was Fyve, a social network built around people’s interests. I chose &lt;a href=&quot;https://go.dev/&quot;&gt;Go&lt;/a&gt;
for the backend because the language felt plain in the right way, and &lt;a href=&quot;https://gin-gonic.com/&quot;&gt;Gin&lt;/a&gt;
started as the web framework for that product. The code still lives at &lt;a href=&quot;https://github.com/gin-gonic/gin&quot;&gt;gin-gonic/gin&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;https://manualmeida.dev/articles/gin/golang-framework-gin-gonic.png&quot; alt=&quot;Gin Gonic Go framework illustration&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Fyve never took off. Gin, the tool I built along the way, is still going twelve years later.&lt;/p&gt;
&lt;h2&gt;Simple over easy&lt;/h2&gt;
&lt;p&gt;At the time, the Go web framework people kept pointing me to was
&lt;a href=&quot;https://github.com/go-martini/martini&quot;&gt;Martini&lt;/a&gt;. I understood why immediately. The README was small,
the middleware model felt elegant, and you could get a route responding in minutes.&lt;/p&gt;
&lt;p&gt;Martini used reflection-based dependency injection to wire handlers together, which made the first
demo feel smooth but also moved important behavior out of sight. Services appeared in your handlers
without any visible wiring, so when something misbehaved the control flow was hard to trace. And all
of that reflection ran on every single request.&lt;/p&gt;
&lt;p&gt;Around then I watched Rob Pike’s
&lt;a href=&quot;https://www.youtube.com/watch?v=rFejpH_tAHM&quot;&gt;Simplicity is Complicated&lt;/a&gt;, and it gave me vocabulary
for what bothered me about Martini. What stuck with me was the idea that simple software often takes
more work from the person building it so that it can take less work from the person using it.&lt;/p&gt;
&lt;p&gt;That became the design brief for Gin. Easy is about how good the first example looks, and Martini’s
first example looked great. Simple is about how many moving parts you have to understand, and how many
exceptions you have to remember, once the codebase is old enough to surprise you.&lt;/p&gt;
&lt;h2&gt;Finding the middle ground&lt;/h2&gt;
&lt;p&gt;Aristotle’s version of virtue was the middle ground: &lt;strong&gt;not too much, not too little.&lt;/strong&gt; That was the shape
of the framework problem too.&lt;/p&gt;
&lt;p&gt;Martini gave you too much magic. Plain &lt;a href=&quot;https://pkg.go.dev/net/http&quot;&gt;net/http&lt;/a&gt; gives you full control
and no surprises, but it helps you with almost nothing, so you end up writing the same plumbing for
route params, request parsing, validation, and responses in handler after handler. None of it is hard,
but it makes the code noisy.&lt;/p&gt;
&lt;p&gt;Gin was my attempt at the point between the two. The request path stays explicit, nothing on it uses
reflection, and the repetitive plumbing lives in a single object called the Context.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;r := gin.Default()
r.GET(&quot;/users/:id&quot;, func(c *gin.Context) {
    id := c.Param(&quot;id&quot;)          // path params, no reflection
    c.JSON(200, gin.H{&quot;id&quot;: id}) // response rendering, one call
})
r.Run(&quot;:8080&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;*gin.Context&lt;/code&gt; carries the request, the response writer, path parameters, validation helpers,
and rendering, so it’s the only thing you pass around. The common operations sit one method call
away, and behind them is plain Go code you can step into when production gets weird.&lt;/p&gt;
&lt;p&gt;Funny enough, &lt;code&gt;gin.Context&lt;/code&gt; shipped in 2014, two years before the standard library’s
&lt;a href=&quot;https://pkg.go.dev/context&quot;&gt;&lt;code&gt;context.Context&lt;/code&gt;&lt;/a&gt; existed. When the standard one arrived we kept our
name and made &lt;code&gt;gin.Context&lt;/code&gt; satisfy the new interface, so every existing program kept compiling and
you could pass a &lt;code&gt;gin.Context&lt;/code&gt; anywhere a &lt;code&gt;context.Context&lt;/code&gt; was expected.&lt;/p&gt;
&lt;p&gt;That instinct came from the SDK years. When the convenient way to do something is also the right way,
people write better code without noticing.&lt;/p&gt;
&lt;h2&gt;A router built around a radix tree&lt;/h2&gt;
&lt;p&gt;The router is where the simple-over-easy line became concrete. Martini matched requests by walking a
list of regular expressions and asking each one whether it matched. Regexes are flexible, since you
can make a route match only numbers or hide extra rules inside the pattern, but they’re also a second
language living inside your framework.&lt;/p&gt;
&lt;p&gt;Gin’s route language is smaller. You get static segments, named parameters, and catch-alls, and that
restriction is exactly what lets the router use a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Radix_tree&quot;&gt;radix tree&lt;/a&gt;, the same approach
&lt;a href=&quot;https://github.com/julienschmidt/httprouter&quot;&gt;httprouter&lt;/a&gt; made popular in Go. It also had a side
effect I came to appreciate. Since the route language gives you nothing to be clever with, routes
written for Gin tend to be regular and boring.&lt;/p&gt;
&lt;p&gt;Matching &lt;code&gt;/blog/42/comments&lt;/code&gt; walks &lt;code&gt;/blog/&lt;/code&gt; down to the &lt;code&gt;:slug&lt;/code&gt; node, binds &lt;code&gt;42&lt;/code&gt;, and continues into
&lt;code&gt;/comments&lt;/code&gt;. The cost of a lookup depends on the length of the URL, and it stays the same whether the
app has ten routes registered or ten thousand. Routes that share a prefix also share nodes, so a big
route table stays compact in memory.&lt;/p&gt;
&lt;p&gt;For a router holding $n$ routes and a request path of length $k$, a radix-tree lookup runs in&lt;/p&gt;
&lt;p&gt;$$
T_\text{match}(k) = O(k), \quad \text{independent of } n,
$$&lt;/p&gt;
&lt;p&gt;while checking a list of $n$ regexes costs $O(n \cdot m)$ for patterns of length $m$. The tree trades
the per-request scan for a single walk down the shared prefix.&lt;/p&gt;
&lt;p&gt;The allocations follow the same thinking. Path parameters go into a preallocated slice, and Context
objects come out of a &lt;code&gt;sync.Pool&lt;/code&gt; and get reset between requests, so the garbage collector has less
junk to clean up and latency has fewer reasons to wobble.&lt;/p&gt;
&lt;p&gt;I trust this kind of performance work because the speed comes from doing fewer things, and doing
fewer things also leaves the person reading the code with less to understand.&lt;/p&gt;
&lt;h2&gt;Designing for zero breaking changes&lt;/h2&gt;
&lt;p&gt;The other constraint I gave myself was backward compatibility. Go had made a
&lt;a href=&quot;https://go.dev/doc/go1compat&quot;&gt;compatibility promise&lt;/a&gt; for the language itself, and I wanted Gin to
offer its users the same deal.&lt;/p&gt;
&lt;p&gt;That constraint changes how you design. Before anything goes into the public API you ask whether
you’d be happy maintaining it for ten years, because removing it later is off the table. You learn to
reject the clever rename that saves five characters, and to treat every exported function as
something a stranger might have built a company on.&lt;/p&gt;
&lt;p&gt;The constraint held. Some of the first programs ever written against Gin still compile and run today,
more than a decade later, and I’m prouder of that than of any benchmark.&lt;/p&gt;
&lt;h2&gt;Hacker News, and then growth&lt;/h2&gt;
&lt;p&gt;I &lt;a href=&quot;https://news.ycombinator.com/item?id=7966700&quot;&gt;released Gin on Hacker News&lt;/a&gt; at the right moment. Go
was getting attention there, and a framework that fit in one README and benchmarked well was easy for
people to try.&lt;/p&gt;
&lt;p&gt;The growth after that was steady. People used it, filed issues, sent patches, and put it in real
services, and today Gin sits around 88k stars with more than 290k projects depending on it.&lt;/p&gt;
&lt;p&gt;Stars are a vanity metric, and I mostly treat them that way. The dependency count is the number I
care about, because each of those projects is a bet that the API won’t break underneath them, and
that bet has kept paying off long after Fyve disappeared.&lt;/p&gt;
&lt;h2&gt;Letting it graduate&lt;/h2&gt;
&lt;p&gt;A few years in, I stepped back and handed Gin to maintainers who kept improving it without me. I
think of that as the project graduating. Special kudos to &lt;a href=&quot;https://github.com/appleboy&quot;&gt;Bo-Yi Wu&lt;/a&gt; and
&lt;a href=&quot;https://github.com/javierprovecho&quot;&gt;Javier Provecho&lt;/a&gt;, who carried it forward and kept the bar high.&lt;/p&gt;
&lt;p&gt;That’s the part of open source I respect most, when a project stops needing its author. Gin has since
absorbed years of other people’s use cases, priorities, and taste, and it came out better for it.&lt;/p&gt;
&lt;p&gt;If you’re building a library, that’s the bar I’d aim for. Design an API you can imagine keeping for
ten years, and make it simple underneath even when that costs more than making it easy, because with
any luck the thing will outgrow you.&lt;/p&gt;
&lt;p&gt;I tried to do the same thing a few years later on a compiler, which is the story of
&lt;a href=&quot;https://manualmeida.dev/articles/qwik-resumability&quot;&gt;Qwik&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://manualmeida.dev/articles/gin-simple-over-easy&quot;&gt;This article has interactive figures — read it on manualmeida.dev.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Open Source</category><category>go</category><category>performance</category><category>http</category><category>open source</category></item><item><title>Half-Truths</title><link>https://manualmeida.dev/articles/notes-i-keep/</link><guid isPermaLink="true">https://manualmeida.dev/articles/notes-i-keep/</guid><description>A text file of one-liners I&apos;ve muttered at a terminal over the years. Half of them are jokes and several contradict each other on purpose. None of them are advice.</description><pubDate>Tue, 28 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;On writing as little as possible&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Software is best without classes, or functions, or code. But if you must, write as little as possible.&lt;/li&gt;
&lt;li&gt;The only thing that really matters in software is KISS.&lt;/li&gt;
&lt;li&gt;That is classic over-abstraction.&lt;/li&gt;
&lt;li&gt;When in doubt, add &lt;code&gt;setTimeout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No hack is complete without &lt;code&gt;.replace()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; is for cowards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On shipping&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Never merge a PR.&lt;/li&gt;
&lt;li&gt;If you’re afraid to release on Friday, the problem isn’t Friday.&lt;/li&gt;
&lt;li&gt;If you can’t release fast, you have tech debt to solve.&lt;/li&gt;
&lt;li&gt;Precommit hooks are the root of all evil.&lt;/li&gt;
&lt;li&gt;The difference between a hobby and a job is that you don’t write documentation for the former.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On things one if-statement away from disaster&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Almost all software is one if-statement away from disaster.&lt;/li&gt;
&lt;li&gt;If you code just to make things work, one day suddenly nothing will work.&lt;/li&gt;
&lt;li&gt;Dependencies are the root of all evil.&lt;/li&gt;
&lt;li&gt;Developers will always find unexpected ways to use your API.&lt;/li&gt;
&lt;li&gt;There are two hard things in computer science: cache invalidation and symlinks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On knowing better&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Debugging skills are underrated.&lt;/li&gt;
&lt;li&gt;You are only an expert in a given technology when you know when &lt;em&gt;not&lt;/em&gt; to use it.&lt;/li&gt;
&lt;li&gt;A good engineer balances not underestimating apparently simple tasks against not overengineering
apparently complex ones.&lt;/li&gt;
&lt;li&gt;An experienced developer is the one who knows when to be volatile and when to be stable. (I figured
it out, by the way: a stable is someone who installs linting build scripts, and a volatile is
someone who disables them.)&lt;/li&gt;
&lt;li&gt;I love monorepos, but I hate lerna.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;If you got this far looking for a coherent philosophy, I’m sorry. The honest version is that good
engineering is mostly knowing which of these to ignore today.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: a good number of these were forged in long, unhinged rants with
&lt;a href=&quot;https://x.com/adamdbradley&quot;&gt;Adam Bradley&lt;/a&gt;. I’ll take half the blame and none of the credit.&lt;/em&gt;&lt;/p&gt;
</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Notes</category><category>notes</category><category>opinions</category><category>humor</category></item></channel></rss>