Reactive programming for differentiable tensors.
∇({ x | Σ(x^2) })([3, 4]) ; [6, 8]The gradient of a scalar field over a vector: Σ collapses the vector to one number, ∇ hands back the slope along each axis. Differentiation over tensors, in one line.
Fluent is a small language and IDE for math you can watch: plots, simulations, models that train. Expressions render, react, and differentiate as you type — on your GPU, in a browser tab.
Open it in the playground — it's the snippet above, live and editable. Every example below opens the same way.
- Reading order is meaning. No precedence table to memorize — the spacing does the grouping. Space a line out evenly and it runs straight left to right:
1 + 2 * 3is9. Close up the multiply and the tighter spacing binds it first:1 + 2*3is7. You read a line the way it runs, and any symbol can become an operator of your own. - Gradients come built in. Build a function out of numeric tensor ops — even one you just wrote — and
∇hands you its gradient. It nests, so∇(∇(f))is the second derivative. - Live by default. Wrap a value in
$( )and it becomes reactive; anything that reads it recomputes when it changes. Drag a slider and only the expressions that depend on it re-run. - Everything shows itself. Results draw themselves — numbers and tensors become plots, heatmaps, and images; quoted code becomes a syntax tree. There's no
print, you just see the value.
| demo | ||
|---|---|---|
| 🌀 | Mandelbrot — the Mandelbrot set over the whole plane, sharper as you drag the depth | ▶ run |
| 🦠 | Game of Life — Conway's Game of Life on a large grid | ▶ run |
| 🔮 | The Name Dreamer — a small transformer that invents new names from a word list | ▶ run |
| 🐦 | Combinators — combine functions without naming their arguments | ▶ run |
| 📷 | Live edge detection — your webcam, edge-detected in real time | ▶ run |
| 🎵 | Live spectrum — your microphone's frequency spectrum, live | ▶ run |
| 🎸 | Pitch detector — the pitch of a note you hum | ▶ run |
| 📈 | Linear regression — a line fit to points by gradient descent | ▶ run |
| 🏔 | Touch the loss landscape — a ball you roll downhill on a loss surface | ▶ run |
| ⚔️ | adam vs sgd — two optimizers descending the same surface from the same start | ▶ run |
| 🌸 | Reaction–diffusion — two reacting chemicals that settle into spots and stripes | ▶ run |
| 🔬 | Lenia — smooth, continuous cellular automata | ▶ run |
| 🧲 | Spinning magnets — an animated vector field | ▶ 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.
Three names for each thing — 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 |
⌈ |
ceil |
TensorCeil |
⌊ |
floor |
TensorFloor |
| — | max |
TensorMax |
| — | min |
TensorMin |
# |
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) |