◆ Flux

What is Flux?

Flux is a total, causal, deterministic application language for the web platform. It is specialized — reactive, visual, deterministic, client-side — over a broad domain, with financial charting and market analytics as the flagship, not the definition. You can write an indicator in one line, an animated scene in five, or a complete interactive application in fifty, and all of them are the same kind of object: a pure dataflow graph over typed streams. A host executes that graph and guarantees — by construction, and verified by machine — that the program terminates, never rewrites its own past, produces the same numbers on every machine, and can touch nothing it was not explicitly granted.

This chapter gives you the shape of the whole language before any of the details: the one idea underneath it, the four planes a program is made of, what programs look like, where they run, whom they are for, and what Flux deliberately does not do. Everything here is unpacked in later chapters, and nothing here assumes prior knowledge of Flux or of trading.

One idea: every value is a stream

A Flux value is not a number sitting in a variable. It is a stream: a value as it evolves along an ordered axis of data units — sensor readings, log entries, game turns, or (in the flagship domain) market bars. close is not “the latest price”; it is the whole history of closing prices, one value per unit, up to now.

Take that seriously and the rest follows on its own:

Here is that difference, running. The two moving averages are streams over all of history; their difference is a third stream, and plot puts it on screen:

FLUX
fast = ema(close, 12)          // a stream: the 12-unit exponential average, over all history
slow = ema(close, 26)
plot fast - slow               // element-wise difference — itself a stream

Because streams are values, ordinary functional composition is the whole programming model: functions from streams to streams (def), records of streams (bb.upper), streams of records, streams driving visual properties. There is one algebra, and you apply it everywhere.

Describe an expression; the engine evaluates

The mental model in one sentence: you describe a pure expression, the engine evaluates it. You never write the loop, the buffer, or the await. The compiler inlines your definitions into a typed incremental DAG (directed acyclic graph) of operations, checks it — kinds, causality, totality, plane boundaries — chooses a native kernel for each node it recognizes, plans its memory statically, and then runs it either per unit (live, one step per new data unit) or in batch (full-history replay). Both runs are the same function and produce the same numbers.

This is why a Flux program has no lifecycle code. There is no “on new bar” callback, no subscription management inside computation, no cache invalidation: the DAG is the dependency graph, and the engine advances it. It is also why the engine can make strong promises — a pure, typed, bounded, acyclic graph is an object you can verify, schedule, parallelize and optimize without changing its meaning.

Four planes, one one-way firewall

A complete application has parts with very different needs. A computation must be exact and reproducible. An animation must read the clock and may use randomness. A screen transition must be able to interpolate freely without corrupting data. Application state must respond to user events and drive effects. Flux does not average these needs into one compromise; it separates them into four cooperating planes — one language, one expression algebra, four sets of rules:

Plane Role Clock Rules
ANALYSIS computation over data units: indicators, signals, representation transforms the data unit (the bar) total, causal, deterministic, no-repaint, sandboxed
CANVAS presentation: animated drawing, decor, effects, pointer interaction the frame screen, wall-time and randomness allowed — outside the guarantees, by design
TRANSITION interpolates the render between two computed states the frame cosmetic by construction: it can never change a value
APP application state and UI: Model · update · view · Sub / Cmd events pure, total, deterministic update; replayable message journal; capability-gated effects

The four planes: computation on the left, presentation on the right, applications below, and the one-way firewall between them. Figure — the four planes: computation on the left, presentation on the right, applications below, and the one-way firewall between them.

The planes are joined by a firewall with a single direction: presentation may read analysis; analysis never reads presentation. A CANVAS scene may glow brighter when an ANALYSIS value rises; an ANALYSIS expression that tries to read the mouse, the wall clock or an unseeded random signal is rejected at compile time with [ErrFirewall]. The APP plane reads series through typed subscriptions and can never write into ANALYSIS.

Why this rule exists. The firewall is what lets guarantees and freedom coexist in one program. Your signal remains provably no-repaint — a value, once produced for a step, never changes — even while an animated, randomized effect dances right next to it, because the language makes it impossible for the effect to feed back into the signal. Every value that matters lives on the disciplined side of the wall; everything decorative lives on the free side.

The chapter The four planes covers each plane in depth.

What a program looks like — three tastes

An analytic, in one line

FLUX
plot rsi(close, input(14))     // rsi : osc(0,100) → own pane, 0–100 scale, guides 30/70

That single line is a complete, shippable program. Everything else is inferred from the kind of the expression — the dimensional type that says what the value is physically, not merely how wide it is:

Nothing about placement, scale, reference lines or parameter plumbing is written anywhere, and none of it needs to be. When you do want control, every one of these defaults is overridable in place — see Your first session.

A scene that moves

FLUX
circle { at: (bar.i, spring(close)); glow: 16; trail: 24 }  // a comet easing toward the price
on every(1 bar) -> spawn ring { at: (bar.i, close); r: 6->24; life: 200 bars }

This is the CANVAS plane. Its axiom: every property is a signal. A constant, a data stream, and an animation generator like spring(close) — a signal that continuously eases toward its target — are the same kind of thing and combine with the same algebra. There is no separate animation API to learn: you wire signals into properties, and events (on … ->) spawn or tween primitives. The scene compiles once into a retained structure; the host routes each signal to the cheapest execution path (cached, per-unit, or compositor-driven). The render path is uniformly GPU — scene graphics and text both draw on WebGPU (sidestepping DOM layout cost), text as SDF glyphs crisp at any zoom — so a moving scene costs bounded, compile-counted work rather than an open-ended frame budget.

An application

FLUX
variant Msg { Inc | Reset }
app counter {
  init(p) = { n: 0 }
  update(m, msg) = match msg {
    Inc   -> { model: m with { n: m.n + 1 }, cmds: [] }
    Reset -> { model: m with { n: 0 }, cmds: [] }
  }
  view(m) = panel(slot: right.panel) { text("count {m.n}") ; button("+1", Inc) }
  subs(m) = []
}

This is the APP plane: The Elm Architecture (TEA), hardened. A Model (bounded, typed state), a pure and total update that folds messages into new state plus a list of commands — inert effect descriptions the host may execute — a pure view that returns a tree of vetted UI primitives, and declarative subscriptions (subs) through which the ambient world (time, data, user gestures) enters as messages. Because update is a pure fold over a message journal, application state is replayable by construction: the journal is the single source of truth. This app requests no capabilities, so the host will let it do nothing beyond drawing its view and receiving its own messages.

One arrow, five readings

You have now seen the arrow -> three times, meaning three different things — an event’s action, an arm of a match, and (in the next chapter) a lambda. That is deliberate, and it is the one piece of syntax worth learning up front, because Flux has exactly one arrow token and it carries five readings, each selected by its context:

One arrow token; the head keyword, or the kind the position expects, decides the reading. Figure — one token; the head keyword, or the kind the position expects, decides the reading.

Reading Example What selects it
lambda vec.map(v, (x) -> x * 1.1) the position expects a function
event → action on click -> burst(40) ring { } the head keyword on
tween pair tween r 6 -> 24 over 300ms no function is expected — the arrow pairs two values
match arm match m.phase { ask -> … } the head keyword match
view comprehension for lvl in levels -> dot { … } the head for … in

The parser never has to guess: a keyword head claims the arrow, and everywhere else the kind of the position decides. The reward is that you never carry a table of arrow-like symbols in your head — there is one, and you read it from its context.

Where programs run

A Flux program has two execution vehicles and one meaning. While you edit, an interpreter evaluates the DAG directly — instant feedback, per-node values the debugger can read, live preview on every keystroke. When a program runs for real — or ships to someone else — it is compiled to a WebAssembly module and executed in a sandbox. The two are not “close”: they are bit-identical. Invariant I7 requires the interpreter and the compiled module to produce exactly the same bytes on the same inputs, and the toolchain verifies this at every compilation by running both and asserting equality — a divergence blocks the artifact from shipping. The same discipline extends across machines: floating-point evaluation is scalar and unreassociated, and every routine with room for platform variance (transcendental math, decimal arithmetic, Unicode, calendars, random generation, the representation of missing values) is pinned to one shared implementation. What you saw in the editor is what runs, everywhere, to the last bit — the same numbers and the same scene on every machine, not identical pixels across every GPU. The machinery is specified in Compiler & runtime.

The language of a platform: two trust tiers

Flux is not an embedded extension niche; it is the language the platform itself is written in, and the language its users extend it in. Both audiences share one language and one sandbox, distinguished only by trust — the capabilities granted, never the code:

Running someone else’s binary safely is a property of the design, not of who wrote the binary: the same guarantees cover any untrusted author, human-written or machine-generated, identically.

The public sharing and marketplace rollout follows v1; the trust model that makes it safe is v1 and is not weakened anywhere.

What Flux deliberately does not do (v1)

Honest limits are part of the design. Each of these is a choice with a rationale, not a gap; the FAQ answers the deliberate non-goals in full.

Where to go from here

Read the chapters in order and the mental model builds cleanly: next comes Why Flux is built this way — the seven guarantees, why each exists and what each buys you — then Your first session, a guided run from one line to a small application. If you read only one more page, read the pillars: every design decision in the language traces back to one of the seven.

See also

The formal rules →