Alpha · the engine and editor are live

A spreadsheet engine
you can put anywhere.

OpenCalc reads and writes real .xlsx, keeps the workbook in a normalized editable model, recalculates it, and renders a virtualized grid — the same Rust core on the desktop, in the browser through WebAssembly, and headless on a server.

Nothing is uploaded — the engine runs on your machine. Apache-2.0.

OpenCalc — running in this page, client-side
347worksheet functions
.xlsxsemantic round-trip, gated
1M+cell capacity target
60 fpsvirtualized scrolling
0network calls

Why OpenCalc

Built like a compiler, not a widget.

Every layer is testable in isolation, and every byte it writes is reproducible. That is what makes it safe to embed in something you ship.

Loss-aware by design

Anything the model can't yet represent — a chart, a pivot cache, a rare style bit — is reproduced verbatim or reported. Never silently discarded.

Deterministic output

The same input and engine version produce the same model, values, layout, and bytes — so both calc and render are regression-tested against golden files.

No host requirements

No mandatory DOM, server, UI framework, or collaboration provider. Rust hosts, wasm32, desktop, and headless services all consume the same core.

A grid that feels native

A canvas grid with real selection, drag-fill, merges, freeze panes, number formats and keyboard navigation — drawn from the engine's own layout, not the DOM.

Safe with untrusted files

Packages are admitted under explicit entry, path, size, expansion and resource limits. No macro execution, no automatic external fetches.

Open, in Rust

A layered crate DAG from model and parser up to layout and render, unsafe forbidden, Apache-2.0. Read every line before you depend on it.

Shipping now

What works today

A verified list, not a roadmap — everything here is exercised by the editor you just used. What is still pending is in the roadmap below.

Editing

  • Inline and formula-bar editing
  • Rich clipboard — formulas and formats travel
  • Drag-fill with reference shifting
  • Find and replace across the sheet
  • Right-click insert / delete rows and columns
  • Undo and redo, one step per edit

Formulas & calc

  • 347 of the 356 functions in the spec
  • Dynamic arrays that spill — FILTER, SORT, UNIQUE, XLOOKUP
  • LET and LAMBDA, with MAP, REDUCE, SCAN and BYROW
  • Autocomplete with signatures; click or drag to insert refs
  • Cross-sheet and whole-row/column references
  • Automatic or manual calculation, as the file asks

Format & structure

  • Fonts, colors, alignment, borders, wrap, rich text
  • Number formats: currency, percent, dates, custom
  • Conditional formatting, data validation, protection
  • Tables with structured references and banding
  • Sort, per-column filter, outlines, freeze panes
  • Page setup, print areas and manual page breaks

Analysis

  • Pivot tables — build, filter, refresh, GETPIVOTDATA
  • Drag fields between Rows, Columns, Filters and Values
  • Eleven aggregates, subtotals and grand totals
  • Charts: column, bar, line, area, pie, doughnut, scatter
  • Insert, retitle, move and resize on the grid
  • Charts written as real chart parts, not pictures

Reads and writes .xlsx · imports and exports CSV, TSV, PSV · formatting, tables, charts, validation, protection, print setup and defined names survive the round-trip — and anything not yet modelled is carried through byte for byte rather than dropped.

Under the hood

One pipeline. Three hosts.

Parsing sits below evaluation, so import and editing use the formula AST without pulling in the calc engine at all.

.xlsx · csv · tsv bounded admission import workbook model recalc layout render · write

Native desktop

A Tauri host where the whole engine — including calc — runs as native Rust at full speed.

WebAssembly

The same core compiled to wasm32, driving a canvas grid entirely client-side. The editor above is the proof.

Headless

No UI at all — batch convert, recalculate, and rasterize a viewport to a deterministic PNG on a server.

Embed the enginecasual-calc-sdk
// One session composes every layer: open, edit, recalc, render, save.
let mut session = WorkbookSession::open(bytes)?;
session.edit(Operation::SetCell { sheet: 0, at: a1("B2"), cell })?;
let png  = session.render_png(0, &viewport)?;   // virtualized grid → PNG
let xlsx = session.save()?;                     // deterministic bytes

Embed it

Ship a spreadsheet inside your product.

One custom element, one line of markup, and your users get the editor above — .xlsx in and out, 347 functions, pivot tables and charts, all client-side. It works in React, Next.js, Vue, Svelte or plain HTML.

Install

The element and, if you use React, its wrapper. No peer dependency on a UI framework.

# the editor as a custom element
npm install @opencalc/sheet

Serve the engine yourself

The WebAssembly binary is copied into your build and served from your origin — never from a CDN we run.

# package.json → postinstall
opencalc-assets ./public/opencalc

Mount it

Give it a height and it boots. Open a workbook, get bytes back, listen for changes.

import "@opencalc/sheet";

<opencalc-sheet style="height:600px">
Load a file, react to edits, save it back@opencalc/sheet
const sheet = document.querySelector("opencalc-sheet");

sheet.theme({ light: { accentColor: "#7c3aed" } });  // CSS tokens, scoped by a shadow root
sheet.chrome({ statusbar: false });                  // hide whole regions…
sheet.commands({ hidden: ["file.open"] });          // …or single controls, by id

await sheet.configure({ access: "view" });          // "edit" | "view" | "preview"
await sheet.open(bytes, "budget.xlsx");

sheet.on("cellsChanged", (e) => {
  if (e.source !== "api") persist(await sheet.save());  // deterministic .xlsx
});

Your CSS cannot reach in and its CSS cannot reach out — the element isolates itself in a shadow root, and theming happens through typed custom properties rather than by overriding our selectors. Read-only and preview refuse writes in the engine, not by hiding buttons. Alpha: published as 0.0.x — everything here works, but the API is not stable until 1.0, so pin what you test against.

Try it

Run the whole thing yourself, before you integrate anything.

Co-editing is not a feature you should have to take on trust. Two containers, one command, and you are sharing a link with a colleague and typing into the same sheet.

Bring it up

The editor, a collaboration server and a reference host. Nothing else to install.

# change OPENCALC_SHARED_SECRET in .env
cp .env.example .env
docker compose up

Share a link

Open localhost:8080, make a spreadsheet, press Share, send the link. You will both see each other's cursors and each other's edits.

# and a cluster, when one node is not enough
docker compose -f docker-compose.cluster.yml up

Take the file away

Download gives you an .xlsx with what you both typed — the server hands the finished bytes back to the host rather than keeping them.

The images are published, so that pulls rather than builds:

docker pull casualoffice/calc        # the collaboration server
docker pull casualoffice/calc-host   # the editor, and your documents
docker pull casualoffice/wopi        # the WOPI adapter, if you need one

Each is linux/amd64 and linux/arm64, and each ships an SBOM and a build-provenance attestation naming the repository, workflow and commit it came from — so what you pull can be traced back to what is here. Add --build to any compose command to build from your own checkout instead.

The two containers are the integration boundary, not a demo artefact. The collaboration server is what OpenCalc supplies — ordering, presence, the socket. The host is what your product supplies — documents, identity, tokens, saving. It holds no per-document state and cannot mint tokens, so it is told, per join, by a party that already knows. That is four endpoints and a signature, and the host you just ran is the worked example of the one you would write.

The self-hosting guide covers reverse proxies, TLS, every setting, monitoring, backup and what the demo defaults are not safe for.

The trade-off everyone else makes

Fidelity, or embeddability. Pick both.

Most options force a compromise: a suite you can't embed, a converter that quietly drops what it doesn't understand, or a web grid that treats the DOM as the source of truth and calculates on a server.

Approach
Embeddable
Offline calc
Loss-aware
Deterministic bytes
A full office suiteExcel, LibreOffice Calc
No
Yes
Yes
No
A converter libraryread or write, no model
Yes
Partial
No
Varies
A web grid + server calcDOM as source of truth
Partial
No
No
No
OpenCalcone Rust core, any host
Yes
Yes
Yes
Yes

Categories, not products — Excel and LibreOffice Calc remain our semantics and fidelity oracles, and we study IronCalc, calamine and Univer openly.

Honest status

Where it is, and what's next.

Delivered in capability-gated phases. The workbook has to be read, modeled, preserved, written back, laid out and rendered before a single cell is evaluated.

Phase 0–1B
Foundation, import and semantic writer
Bounded XLSX reader, normalized model, preservation ledger, and a round-trip that is a gated semantic fixed point.
Done
Phase 1C–1D
Grid layout and render
Column and row geometry, merges, number-format display, display list, CPU raster and viewport virtualization.
Done · glyph shaping pending
Phase 1E
Browser grid editor
Canvas grid over the WebAssembly engine: selection, formatting, structural ops, undo and redo.
Done
Phase 2
Formula and calc engine
347 of the spec's 356 functions, dynamic arrays, LET and LAMBDA. The nine that are missing each need something this cannot have — a live OLAP cube, a COM server — and are named rather than counted. The incremental dependency graph and the sub-50 ms worst case are the remaining work.
In progress
Phase 3
Spreadsheet features
Conditional formatting, data validation, protection, tables and structured references, autofilter, print setup, charts and pivot tables — all shipped. A pivot built here still exports as its cells rather than as a live Excel pivot; that part of the writer is deliberately staged.
Done · one gap named
Phase 4–6
SDK beta, collaboration, 1.0
The host surface now takes a configuration: admission limits for untrusted files, calculation mode, the clock and seed calculations read, and how far undo reaches. Next is the concurrency model for shared editing, then support guarantees.
In progress

Open the editor and start typing.

No sign-up, no upload, no build step. Bring an .xlsx and watch it round-trip — then put the same thing in your own product.

Contributing

Apache-2.0, CI-gated, fuzzed and benchmarked. Fidelity bugs with a fixture attached are the most valuable thing you can send.

cargo test --workspace