Impromptu under the hood
A companion to the launch manifesto for readers who watched a demonstration and want to know what is actually running. The manifesto says what Impromptu is for; this note opens the hood and names the parts. It is written for the tech-curious — a working knowledge of spreadsheets is enough — and every technical term is collected in the Glossary at the end and explained where first used.
Impromptu is the prototype modeling engine at the center of this workbench: a multidimensional spreadsheet in the tradition of Lotus Improv, where formulas are written over named things rather than cell addresses, and the structure of a model is something you can read. If you have seen one of the demonstrations, you have seen the surface — a grid, pivotable views, formulas that say Revenue × Margin, an AI assistant setting assumptions and drafting a report while the model updates on screen. This note is about what sits beneath that surface.
A word before the tour, because it matters for what follows. Impromptu is not a fork or a clone of any commercial product. It grew out of an earlier prototype of my own, and it takes a deliberately different architecture — one that would make little sense for a shipping product but makes a great deal of sense for a research instrument I want to take apart. It also reaches into territory a modeling product has no reason to go: scientific computing and small machine-learning models, wired in as first-class citizens. Keep that in mind as the pieces come into view; by the end it should be plain why this is a personal laboratory, not a market player.
1. Two tiers and a bench
Impromptu is split into two programs that talk over the network.
- A Julia server holds the model and does all the computation — parsing formulas, evaluating them, cascading changes, keeping the numbers correct.
- An Elm client, running in the browser, is the surface you interact with — the grid, the pivot views, the dialogs.
The server is the single source of truth. The client keeps a local copy for responsiveness, but every real change is a message to the server, which recomputes and pushes the result back. Mutations travel as JSON over HTTP; the server pushes updates back over a WebSocket — a connection kept open so the server can speak first, the moment anything changes, without the client having to ask. Everything lives on one port (:8090), so “the app” is really one Julia process serving both the API and the browser.
A third participant joins through a side door: an AI assistant. It does not talk to the browser at all — it talks to the same Julia engine, through a small protocol server of its own. That gives the whole system its shape:
This two-tier split — a computation server, a thin reactive client, an AI on the same engine — is the first thing that makes Impromptu structurally unlike a desktop modeling application. The next sections are really an answer to one question about each tier: why that technology?
2. Why Julia
The engine is written in Julia, and the choice is not incidental — several of Impromptu’s ideas are only comfortable because Julia is the host.
Labeled multidimensional arrays. A multidimensional model is, underneath, a stack of N-dimensional arrays whose axes have names (“year”, “account”, “scenario”) rather than bare numeric positions. Julia has a library for exactly this — DimensionalData.jl, whose DimArray is the Julia counterpart to Python’s xarray: an array you index by label, not by offset. Every Impromptu data array is a DimArray under the hood. When a formula says “the 2024 column of Revenue”, it resolves to a labeled slice, not a fragile row number. Building on a mature scientific-computing library — rather than inventing a bespoke cell store — is what lets the same engine host a financial forecast and a matrix computation without switching gears.
A dynamic, interpreted language — which turns out to matter for the AI. Julia compiles, but it feels interpreted: you can build and evaluate new code at runtime. A formula in Impromptu is not interpreted by a hand-written expression evaluator; it is turned into real Julia code and compiled (more on that in §4). The same property is what lets an AI assistant reach into a running model and do things — evaluate an expression, compute an IRR, reshape a dataset — without recompiling and restarting the application. The engine is a live environment the assistant can operate inside. A static, ahead-of-time language would have made that awkward; a dynamic scientific language makes it natural.
Reach a product wouldn’t build. Because the engine sits on Julia’s numerical ecosystem, things that are out of scope for a spreadsheet product come almost for free: linear algebra, optimization, statistics, and small machine-learning models trained inside the model as modeling exercises. That reach is the point of the experiment — and, incidentally, the clearest sign that Impromptu is aimed somewhere different from a commercial modeler.
3. Why Elm
The browser client is written in Elm, a small, strictly typed functional language that compiles to JavaScript. Impromptu began life as an Elm-only prototype — the interface with an embedded formula engine came first, and the Julia engine grew up behind it — so the choice is partly lineage and partly conviction.
The conviction is about robustness. A spreadsheet UI is a dense, stateful, reactive thing: thousands of cells, live updates arriving over a socket, pivot operations rearranging the whole grid. Elm’s design makes a class of runtime failures structurally impossible — there are no null-reference exceptions, and every possible message the interface can receive must be handled explicitly. In practice that means the client can absorb a stream of server pushes and user actions without drifting into the inconsistent, half-updated states that plague hand-written reactive UIs. For a tool whose whole promise is that the machinery stays legible and trustworthy, a UI that cannot silently break is worth a great deal.
The layout is built with elm-ui, a library that replaces raw HTML-and-CSS with a typed layout language; the data grid itself drops down to plain HTML for performance, but the scaffolding around it is all typed Elm.
4. The formula language: friendly by default, terse on request
This is where Impromptu tries hardest to be familiar to a finance modeler and honest to a programmer at the same time.
A formula is attached to a whole array, not to individual cells — the Quantrix and Improv paradigm. You write one rule, Gross = Revenue - COGS, and it applies across every year, scenario, or product the array spans. To pick out a coordinate, there is a concise bracket sugar borrowed from that tradition:
SPA.delta[Crediti verso clienti netti]
reads a single labeled row without ceremony. Under the surface that is the same as the verbose call form SPA.delta(Voce="Crediti verso clienti netti") — the bracket is an affordance, not a different language.
That is the friendly mode, the default. It adds a handful of quality-of-life rewrites on top of plain Julia: arithmetic broadcasts across dimensions automatically, mean and var become their blank-safe variants, and excess dimensions are summed away to match a target shape — the small courtesies that make multidimensional arithmetic behave the way a modeler expects rather than the way a matrix library insists.
For users who are comfortable with matrix notation — the Matlab and R crowd — there is an algebra mode that steps out of the way and accepts Julia’s terse linear algebra directly:
variance = weights' * covmatrix * weights
Same engine, fewer courtesies, full control.
The important part is what happens to either one. Every formula, in every mode, is compiled to real Julia code. The path is worth stating plainly, because it is the heart of the engine:
- Parse the formula text into a Julia expression.
- Validate it against a safety list — expressions that try to reach
eval,include,ccall, or the language internals are rejected before they can run. - Rewrite it through a ten-pass transformation that resolves names to labeled arrays, applies the friendly-mode sugar, handles the positional operators (“previous period”, “first”, “last”), and inserts broadcasts.
- Compile the rewritten expression, once, into a cached function (a closure) keyed to the dataset. Recalculation just calls the cached function — which is why re-running a model is fast even when the formulas are non-trivial.
There is no hand-rolled formula interpreter to trust or distrust; a formula is Julia, and the friendly syntax is a thin, well-defined layer that melts into it. When the model has circular dependencies — a financing plug that depends on interest that depends on debt that depends on the plug — the engine finds the cycle and iterates it to convergence, rather than throwing up its hands.
5. Typed cells, and an escape hatch called Object
Every cell in Impromptu has a type, and this is more consequential than it sounds. The base types are the ones you would expect: number, text, date, and datetime. Knowing a column is a date, not a string that looks like one, is what lets date arithmetic and day-count conventions just work.
Then there is a fifth type that opens the ceiling: Object. An Object cell can hold any Julia value — a struct, a vector, a dictionary, a whole matrix, a loan schedule, a fitted model. It is always formula-computed (you cannot type one in by hand), and it lets you stitch a computation together across cells: one cell builds a loan’s amortization schedule as a structured object, the next cell reads a field off it — schedule.endDate — and a third charts the cash flows. The spreadsheet stops being a grid of numbers and becomes a place to lay out a computation whose intermediate results happen to be rich data structures. This is the feature that most clearly marks Impromptu as a scientific-computing environment wearing a spreadsheet’s clothes — and it is only possible because the engine is Julia all the way down.
6. An AI at the bench
Impromptu is built to be operated with an AI assistant, and the way that connection is made is a design decision I care about.
The assistant reaches the engine through an MCP server — a small program speaking the Model Context Protocol, the emerging standard for giving AI models structured access to tools. Through it, the assistant can open a model, inspect datasets, and change them, while you watch every step in the browser.
At one extreme, the assistant can run arbitrary Julia against the live engine — there is an eval tool that is exactly that, and it is genuinely powerful. But raw power is not the design; discipline is. The everyday work goes through a small, deliberately narrow set of actions — create a dataset, add a dimension, add arrays, set formulas, set values, clone a dataset — six typed operations with validation and clear error messages, exposed as their own tools. The engine ships with modeling skills: written guidance that directs the assistant to reach for those principled actions rather than improvising with eval. The effect is that the AI builds models the way a careful modeler would, one well-defined move at a time, and the free-form escape hatch stays available for genuine computation and analysis rather than being the default.
That is the “legible even when an AI has its hands on it” idea, made concrete. The assistant is not a black box bolted onto the side; it operates the same authoritative engine the humans do, through a channel whose moves you can name.
For the record: Impromptu’s own code was written this way too — by me, working with Claude Code (Anthropic’s Sonnet and Opus models) as a pair, one reviewed change at a time.
8. What Impromptu is, and what it isn’t
So, to close the hood.
Impromptu is a personal, private research project. Its purpose is to produce demonstrations and deliverables that circulate among a small circle of software developers and power users — not a product, not a download, not an open-source release. It was built on a pre-existing codebase of my own; it takes an architecture (a Julia scientific engine, a typed reactive client, an AI-native control surface) that a commercial modeler has no reason to adopt; and it spends its energy on things — labeled scientific arrays, embedded machine learning, an AI operating the engine — that lie outside what a modeling product sets out to do.
The multidimensional tradition it belongs to is very much alive, and I still teach and work in Quantrix Modeler — it is the standard that made this experiment conceivable, and nothing here changes that. Impromptu is not a replacement for it. It is a bench where I can take the ideas apart, wire an AI into the middle of them, and see what holds. The point is the experiment; opening the hood is part of it.
Glossary
Multidimensional spreadsheet
A spreadsheet where formulas are written over named dimensions (years, accounts, scenarios) and apply across a whole array at once, rather than being copied cell by cell. The lineage runs from Lotus Improv to Quantrix Modeler.
Two-tier architecture
A design split into a server (which holds the data and does the computation) and a client (the user interface). Here the Julia server is authoritative; the Elm browser client is a synchronized view.
HTTP / WebSocket
HTTP is the ordinary request-and-response protocol of the web: the client asks, the server answers. A WebSocket is a connection left open in both directions, so the server can push a message the instant something changes without waiting to be asked — how live updates reach every client.
Julia
A programming language for scientific and numerical computing. It compiles for speed but behaves interactively, and it can build and run new code at runtime — the property that makes both the formula engine and the live AI connection natural.
DimensionalData / DimArray
A Julia library for labeled multidimensional arrays — arrays you index by axis name and coordinate (“the 2024 column of Revenue”) instead of by numeric position. The Julia counterpart to Python’s xarray. Every Impromptu data array is one of these.
Elm
A strictly typed functional language that compiles to JavaScript. Its design rules out whole classes of runtime errors (no null-reference crashes; every message must be handled), which makes for an unusually robust reactive user interface.
Array-level formula
A formula attached to an entire data array rather than to a single cell. One rule (Gross = Revenue - COGS) applies across every coordinate the array spans.
Friendly / algebra mode
Two ways to write the same formulas. Friendly (the default) adds conveniences — automatic broadcasting across dimensions, blank-safe statistics — on top of plain Julia. Algebra mode steps out of the way and accepts terse matrix notation directly. Both compile to the same Julia code.
AST / ten-pass rewrite
An AST (abstract syntax tree) is the structured form of a parsed expression. Impromptu rewrites each formula’s AST through a sequence of ten transformations — resolving names to labeled arrays, applying sugar, handling “previous/first/last” operators, inserting broadcasts — before compiling it.
Closure (cached)
A compiled function the engine builds once from a formula and reuses on every recalculation, keyed to its dataset. Caching these closures is what keeps re-running a model fast.
Object type
A cell type that can hold any Julia value — a struct, vector, dictionary, matrix, or a domain object like a loan schedule. Always formula-computed, it lets a model stitch together a computation whose intermediate results are rich data structures, not just numbers.
MCP (Model Context Protocol)
An open standard for giving an AI model structured access to external tools. Impromptu’s MCP server is the channel through which an assistant reads and operates a live model.
eval vs. actions
eval runs arbitrary Julia against the engine — maximum power, minimum structure. The six actions (create dataset, add dimension, add arrays, set formulas, set values, clone dataset) are narrow, validated operations the assistant is guided to prefer, so that model-building stays disciplined and legible.
Sources & links
- Impromptu in action — demonstrations live on the YouTube channel.
- The manifesto — Welcome to The Workbench sets out what the project is for.
- The technologies — Julia, DimensionalData.jl, Elm, the Model Context Protocol, and the tradition of Quantrix Modeler.