cd /news/artificial-intelligence/noiselang-where-n-5-is-a-dirac-delta · home topics artificial-intelligence article
[ARTICLE · art-51039] src=manualmeida.dev ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

NoiseLang: Where N = 5 is a Dirac delta

NoiseLang, a programming language where every value is a probability distribution, has been revived after nine years using AI tools. The language compiles to a JIT runtime via Cranelift and a WASM backend, enabling efficient Monte Carlo simulations. Its creator, a telecommunications graduate, built the ambitious version with AI assistance for runtime components while handling language design manually.

read7 min views6 publishedJul 6, 2026
NoiseLang: Where N = 5 is a Dirac delta
Image: Manualmeida (auto-discovered)

During my telecommunications degree 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.

That wish became NoiseLang. 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.

Everything is a distribution #

The whole language hangs on one idea, that every value is a probability distribution. 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.

A name always refers to one fixed node, the same way X

is the same X

across a whole page of math. So X + X

is 2X

and X - X

is exactly 0

,. If you want variable independence you write separate draws, ie, using ~

multiple times, or ~[N]

to draw N

independent variables into a vector.

X ~ unif_int(1, 6)
Y ~ unif_int(1, 6)
X + Y                 # two independent dice, a real 2d6 distribution

Nothing runs until you ask for some results, for example, P(X + Y < 10)

, 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.

Bday = unif_int(1, 365)
days ~[23] Bday          # 23 people in a room
P(has_duplicates(days))  # the birthday paradox, about 0.507

This is much easier to watch than to describe, so I built some cool demos!

Why it sat for nine years #

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 a efficient Monte Carlo runtime, instead of a naive interpreter, conditional bayesian inference, and more.

Current version is a compiler, a JIT (using the amazing Cranelift), a WASM backend, and a pile of careful numerical code, so for a cute-weekend project it stayed permanently out of reach.

Building the ambitious version with an agent #

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).

With NoiseLand, 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 with with different syntax for non-orthogonal features.

One IR, three backends #

Under the hood, ~

and the distribution constructors build an append-only DAG called the RvGraph. This graph is the single source of truth, which later are converted into three different code paths:

  • a columnar batch interpreter that works everywhere and acts as the correctness oracle;
  • a Cranelift JIT that fuses a whole expression into one native kernel;
  • a WASM emitter that does the same for the browser.

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.

Making the Monte Carlo loop cheap #

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).

Kernel fusion keeps every intermediate value in registers, so an arithmetic-heavy expression stay on registers. The PRNG (xoshiro256++) compiles into the kernel, and the ln

, sin

, and cos

become inline polynomial approximations, speeding up the kernel by a factor of 2.

My favorite trick is in the RNG. Generating random numbers is a serial dependency chain, so instead of fighting that, the kernel runs four independent streams at once and lets the out-of-order core overlap them. This trick ended up beating a hand-written SIMD kernel!

On my 14-core M4 Pro, a one-line P(...)

sustains around 5.8 billion samples per second and scales about 9.6× from one core to all of them. Per core, the generated kernel runs within about 1.15× of hand-written Rust compiled by LLVM. The same fused loop, emitted as WASM, runs at roughly half to three-quarters of native speed inside V8.

Where Noise sits #

NoiseLang is a toy language, you probably should not use it for anything serious, however I wish this language existed during my university days.

For a language nerd, it’s a small, static random-variable algebra with forward Monte Carlo, expression-based, rejection-based conditioning, language.

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.

Noise NumPy Stan PyMC
What it is RV algebra + Monte Carlo array numerics declarative Bayesian modeling Bayesian modeling in Python
Core abstraction every value is a distribution n-d arrays data / parameters / model blocks with Model() context
Getting an answer P / E / Var / Q force sampling you write the loop compile → NUTS → summarize pm.sample() → arviz
Inference forward MC + rejection conditioning none (you build it) HMC / NUTS at scale HMC / NUTS, VI, SMC
Setup to first answer zero — type in a browser import numpy install toolchain, compile install stack, build a context
Visualization built into the language matplotlib (separate) bayesplot (external) arviz (separate)
Runs in the browser yes — WASM, no install no no no

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). Where Noise wins when you have a probability question and you wanna know the answer without much hassle.

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.

Back to signals and noise #

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”.

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.

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.

Run NoiseLang in the browser #

Everything above runs on @noiselang/core, thanks to the Rust engine compiled to WebAssembly.

npm install @noiselang/core
js
import { run } from "@noiselang/core";

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

console.log(result.value); // "3.1415…" — the last statement's value
console.log(result.output); // everything Print(...) emitted

run

never throws, failures come back on result.error

with a source span. There is also runWithIntrospection

, the API behind the variable inspector at noiselang.com.

NoiseLang is playable in the browser at noiselang.com. Open it, type X ~ unif(-1, 1); Y ~ unif(-1, 1); 4 * P(X^2 + Y^2 < 1)

, and watch a few million draws estimate π from your browser tab.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @noiselang 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/noiselang-where-n-5-…] indexed:0 read:7min 2026-07-06 ·