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:
- A constant is a degenerate stream — the same value at every unit.
2andcloseare the same kind of thing, so they combine freely. - Arithmetic is element-wise.
fast - slowsubtracts two entire histories, unit by unit — yet the engine evaluates it incrementally, one new value per new unit. - There is no index and no loop. You never write
for i, never manage a buffer, never decide when to recompute. You describe what a value is, not when to update it.
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:
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 streamBecause 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 |
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
plot rsi(close, input(14)) // rsi : osc(0,100) → own pane, 0–100 scale, guides 30/70That 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:
closehas kindprice;rsimaps any scalar quantity toosc(0,100)— a bounded, dimensionless oscillator.- An
osc(0,100)shares no scale with price, so it materializes in its own pane rather than on the price chart — with a fixed 0–100 scale. - The conventional guide lines at 30 and 70 come from the operation’s metadata and are drawn automatically.
input(14)declares a parameter: the editor derives a control for it, the tuned value lives with the chart instance (the source keeps the default), and adjusting it re-runs the graph without recompiling.- The series is registered — name, render style, semantic class, even its accessibility description — all derived from the kind.
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
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
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:
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:
- Tier A — first-party. The platform’s own interface, including its most demanding tools, is written in Flux. Its source stays private and ships as compiled WASM — the strongest possible statement that the language is sufficient for real applications.
- Tier B — authors and users. Anyone can write indicators, representations, drawing tools and applications. A shared or purchased artifact arrives as WASM only, never source: the author’s work is protected, and the consumer runs an untrusted binary safely — because scripts are total (they cannot run away), sandboxed (the language has no I/O primitives to abuse), and default-deny: every effect requires a capability, and an artifact carries an inspectable, transitively aggregated manifest of everything it may ask for, visible before installation.
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.
- No free-running loops. Flux is total, not Turing-complete — by choice. A total program is
unbounded over time but bounded per step: windows, folds and loops exist, but every bound is
a compile-time constant under a cap. A program that cannot state its bound is rejected with
[ErrTotal]at compile time rather than killed by a timeout at runtime. In a client-side reactive domain the trade is invisible — an unbounded inner loop there is a hung tab, i.e. a bug. (The design weighs an opt-inunsafeescape for an unbounded loop and turns it down by default; nothing in the catalogue needs one.) - No direct I/O from scripts. The language has no fetch, no DOM, no eval, no file handles. Effects are host-mediated: a script emits inert command data under declared capabilities, and the host executes it. This is what makes running untrusted code a routine act rather than a risk assessment.
- Bar-centric time grain. The ANALYSIS clock advances on closed data units. Tick-level and order-flow granularity live in a separately named extension whose rollout follows v1, not an implicit promise.
- External non-price data stays out of ANALYSIS. Network-fed data enters the APP plane through
typed subscriptions; it cannot silently become an “indicator”. The
metrickind names the seam through which such a stream could enter ANALYSIS — carried in the design and held inert, so admitting one later changes no grammar. - No market-wide scan, no portfolio management. Cross-series work over a handful of named instruments is first-class; scanning the whole market, and managing many simultaneous positions, are not v1 concerns. (A bounded screener — a total function mapped over a fixed-capacity universe, with a keyed top-K — is a sealed pillar whose rollout follows v1; it is a bounded map-reduce, never an unbounded scan.)
- No automatic code migration. Flux does not ship a converter from other script ecosystems. Its semantics deliberately exclude patterns some ecosystems permit (retroactive history edits, unbounded loops, ambient I/O), so a faithful automatic translation is impossible for a whole class of programs; the documentation instead teaches the equivalent Flux patterns directly.
- Sealed, delivered after v1. Strategy backtesting harnesses, remote alert delivery, and
multi-device sync are sealed designs whose rollout follows v1; local alerts (
alert) are v1.
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
- Why Flux is built this way — the seven guarantees behind everything above.
- Your first session — write your first program in the next ten minutes.
- The four planes — the plane model and the firewall, in depth.
- Guarantees — what is promised and how each promise is machine-verified.
- The FDK — the author-facing API surface and the capability model.
The formal rules →
- Grammar — the single arrow token and its five readings, disambiguation, formal properties.
- Kinds and Inference — how a kind decides a value’s pane, scale and guide lines.
- Time & state — streams, causality, the incremental DAG, no-repaint.
- The Canvas plane and The App plane — signals-as-properties, the firewall, the TEA core.
- Compiler & runtime — interpreter ≡ WASM (I7), pinned routines, compile-time budgets.
- Host services & capabilities — the capability catalogue behind default-deny and the manifest.