Reactive programming for differentiable tensors.
Fluent is a tiny language + IDE for people who like to tinker with math — plots, simulations, little machines that learn. You write plain expressions; they render, move, and differentiate as you type.
▶ Open the playground — nothing to install, and it's fast: your GPU does the work, right in the browser.
▶ run this — every snippet in this README is a link that opens live in the playground.
- Reading order is meaning. There is no precedence table: spaced operators run left-to-right, glued ones bind tighter — that's the whole story. Code you've never seen evaluates the way you read it, pieces compose without surprises, and any glyph can become an operator of your own.
- Everything is differentiable.
∇works on any function, including ones you just wrote. Higher-order too:∇(∇(f)). - Everything is reactive. Drag a slider and exactly the expressions that depend on it re-run — nothing else, nothing forgotten.
- Results are visible by default. Tensors render as plots, heatmaps, and images; the AST renders as a tree; there is no
print.
| demo | ||
|---|---|---|
| 🌀 | Mandelbrot — the whole plane iterated at once with ⍣; scrub the depth |
▶ run |
| 🦠 | Game of Life on a 256² torus — three lines of rules | ▶ run |
| 📷 | Live edge detection — your camera through a Laplacian kernel | ▶ run |
| 🎵 | Live spectrum of your microphone — whistle and watch the peak move | ▶ run |
| 🎸 | Pitch detector — hum a note, it tells you which one | ▶ run |
| 📈 | Linear regression — fit a line by gradient descent, loss falling live | ▶ run |
| 🏔 | Touch the loss landscape — grab the ball, drag the learning rate, watch gradient descent roll | ▶ run |
| ⚔️ | adam vs sgd race down Himmelblau — same start, different characters | ▶ run |
| 🌸 | Reaction–diffusion — Gray–Scott chemistry paints Turing patterns; drag feed and kill | ▶ run |
| 🔬 | Lenia — continuous cellular automata; smooth, life-like blobs emerge | ▶ run |
| 🧲 | Spinning magnets — an animated field from outer products | ▶ run |
| 🧠 | Attention, built by hand — drag the temperature, watch softmax sharpen | ▶ run |
| 🎓 | Turing's B-type machine (1948) learns XOR by backprop — it isn't in its toolbox, so it composes it | ▶ run |
| 🔢 | A differentiable logic-gate network learns MNIST in your tab — no neurons, just soft boolean gates | ▶ run |
More in the playground: press Ctrl+O for the full gallery.
Spacing is grouping — hug what belongs together:
1 + 2 * 3, ; 9 — spaced operators run left-to-right
1 + 2*3 ; 7 — glued operators bind tighterTensors are the only numbers — a scalar is just a small one, arithmetic broadcasts, and a glued _ indexes:
v: [10, 20, 30],
v + 1, ; [11, 21, 31]
v_0, ; 10 — a glued _ indexes
v_(-1), ; 30 — from the end
v_[2, 0], ; [30, 10] — with a tensor of indices
Σ(v) ; 60 — and Σ, μ, sort, fft, conv… are built inFunctions and operators are the same thing — anything can be called, anything can sit between its arguments:
1 + 2, ; 3
+(1, 2), ; 3 — an operator, called
1 add 2, ; 3 — a function, infix
1 {x, y | x + y} 2 ; 3 — even a lambdaDefining your own operator is just a binding: (++): ListConcat.
Everything has three names — long for discovery, a word for habit, a glyph for fluency:
TensorSum(0 :: 10), ; 45
sum(0 :: 10), ; 45 — same function
Σ(0 :: 10) ; 45 — same hover cardLong names make things findable; the more you use one, the shorter you want it. TensorGradient is grad is ∇ — all in scope, all sharing one doc card, so your notation can tighten as you go. And names are unicode throughout: θ, 𝓛, ŷ are fine.
the name tiers at a glance
| glyph | word | full name |
|---|---|---|
∇ |
grad |
TensorGradient |
Σ |
sum |
TensorSum |
Π |
prod |
TensorProduct |
μ |
mean |
TensorMean |
⌈ |
max |
TensorMaximum |
⌊ |
min |
TensorMinimum |
# |
length |
TensorLength |
_ |
gather |
TensorGather |
⍴ |
reshape |
TensorReshape |
:: |
range |
TensorRange |
⊗ |
outer |
TensorOuter |
^ |
pow |
TensorPower |
√ |
root |
TensorRoot |
% |
mod |
TensorRemainder |
÷ |
div |
TensorDivide |
× |
mul |
TensorMultiply |
~ |
var |
TensorVariable |
:= |
— | TensorAssign |
⟳ |
iter |
FunctionIterate |
⍣ |
— | FunctionPower |
. |
apply |
FunctionApply |
@ |
eval |
FunctionEvaluate |
$ |
— | Reactive |
| — | watch |
TensorWatch |
| — | conv |
TensorConvolution |
| — | once |
SignalOnce |
Some cells are still empty — the language is young, and names are earned.
Signals make it live — $(…) makes a signal; whatever touches it recomputes when it changes:
x: $(0.5),
(Slider(x), x ^ 2)Training is a few lines — ~ makes a trainable variable, an optimizer (sgd, adam, adamw, adagrad) minimizes a loss thunk, ⟳ runs it between frames so the UI stays live:
θ: ~([0, 0]),
𝓛: { Σ((θ - [0.23, 0.47])^2) },
opt: sgd(0.1),
{ opt(𝓛) } ⟳ 100,
θThe full tour lives in the playground — open the built-in Documentation, or hover any built-in for its card.
Live evaluation on every keystroke, hover docs, unicode completion (type alpha, get α), syntax trees for quoted code, camera and microphone as tensor sources, and LLM code generation — write ;;a bouncing ball;; and it appears (bring your own Anthropic API key, set via the command palette).
Ctrl+O examples · Ctrl+S share as URL · Ctrl+P commands · Ctrl+Space complete — Safari reserves ⌘O, use ⇧⌘O there
git clone https://github.com/mlajtos/fluent.git
cd fluent && bun install
bun dev # → http://localhost:3000The whole thing fits in a handful of files — read it, change it:
| file | what |
|---|---|
language.ts |
the language — grammar (Ohm), evaluator, prelude; tensors via jax-js, reactivity via preact signals |
client.tsx |
the IDE — components, visualizers, Monaco editor, playground |
tests.ts |
language tests (bun test ./tests.ts) |
tests.browser.ts |
IDE tests in real Chromium (bun run test:browser) |
