World model¶
The world is a function, not a data structure: given a seed and an address it returns what is there. Nothing is stored for the whole field, only the blocks around the window being imaged are ever evaluated and cached.
Address-based randomness¶
All randomness comes from pcg4d [jarzynski2020], a counter-based hash on four
uint32 words:
A draw depends only on its own address, never on how many draws came before it. Consequences:
- Panning to \((10^6, -4\cdot 10^5)\) um costs the same as the origin; revisiting a place reproduces it exactly.
- Queries can run in any order and on any number of threads with the same result (tested: 8 threads = 1 thread).
- A fixed channel (purpose) number per kind of draw keeps independent decisions independent.
Caches exist for speed only. The test world_checks verifies results are independent of query history, cache size and
dropped caches.
Cells¶
The plane is divided into chunks (default 26 µm). Each chunk has at most one candidate cell centre, jittered within the chunk (a stratified-jitter point field: O(1) per chunk, no global state, unlike Poisson-disk sampling). An occupancy draw decides whether the candidate becomes a cell.
Cell outline: an ellipse (random elongation, rotation) whose geometric mean diameter is drawn from a range, modulated by
a few angular harmonics ("blobbiness") to look like a confluent-culture cell. Keeping the diameter meaningful at any
blobbiness needed three separate fixes (a symmetric clamp, a numeric area correction per cell, and a per-cell modulation
floor instead of whole-cell scaling); they are described in spec/ALGORITHM.md and must not be "simplified" away.
On top of the lobes the outline has a fractal wiggle: harmonics 6-64 with amplitudes \(\propto k^{-(2.5-D)}\), so a
box-counting dimension \(D\) (a self-affine outline has \(D = 2 - H\) for Hurst exponent \(H\)
[gneiting2004], and amplitudes \(\propto k^{-(H+1/2)}\)). cellFractalDim defaults to
1.35: box counting on the contours of cultured MCF10A epithelial cells gives \(D = 1.352 \pm 0.044\), whatever their shape
[kim2015]. The wiggle is scaled by the blobbiness (cellRough x cellBlob: a round cell
stays smooth).
The outline built up term by term on one cell, from the cli's geometry output:
How this figure was made (5 cli runs, 76 ms)
Generated by tools/build_physics_figures.py (figure world-outline) from the code it documents at commit 72b1b0f, on Linux with 4 threads, 2026-11-01. Commands, in order, with their wall time (those marked [served] ran one after the other in one insiliscope_cli --serve process, which keeps the world and the PSF kernel between commands; their outputs are the same as separate runs'):
insiliscope_cli --geometry-json ellipse.geometry.json --x 6.58 --y -5.61 --geometry-um 60 --geometry-detail 0 --packing 0 --p.cellBlob 0 --p.cellRough 0 --npc-dye None # 13 ms [served]
insiliscope_cli --geometry-json lobes.geometry.json --x 6.58 --y -5.61 --geometry-um 60 --geometry-detail 0 --packing 0 --p.cellRough 0 --npc-dye None # 6.5 ms [served]
insiliscope_cli --geometry-json fractal105.geometry.json --x 6.58 --y -5.61 --geometry-um 60 --geometry-detail 0 --packing 0 --p.cellFractalDim 1.05 --npc-dye None # 14 ms [served]
insiliscope_cli --geometry-json fractal.geometry.json --x 6.58 --y -5.61 --geometry-um 60 --geometry-detail 0 --packing 0 --npc-dye None # 18 ms [served]
insiliscope_cli --geometry-json fractal18.geometry.json --x 6.58 --y -5.61 --geometry-um 60 --geometry-detail 0 --packing 0 --p.cellFractalDim 1.8 --npc-dye None # 25 ms [served]
Packing - move, don't shrink. Cells are never resized to avoid overlap. A fixed number of Jacobi relaxation iterations moves overlapping neighbours apart by half the overlap using a same-iteration snapshot, so a pair separates symmetrically and independent of evaluation order. Cells still stuck afterwards are removed by a per-candidate priority hash, never shrunk. Packing is chaotic: a \(10^{-15}\) difference in a position changes which cells survive, which is why the geometry has to be bit-exact between implementations.
Packing runs on fixed 8x8-chunk blocks, so the result for a cell does not depend on the viewport.
The same field without and with the packing step:
How this figure was made (2 cli runs, 592 ms)
Generated by tools/build_physics_figures.py (figure world-packing) from the code it documents at commit 72b1b0f, on Linux with 4 threads, 2026-11-01. Commands, in order, with their wall time (those marked [served] ran one after the other in one insiliscope_cli --serve process, which keeps the world and the PSF kernel between commands; their outputs are the same as separate runs'):
insiliscope_cli --geometry-json packing0.geometry.json --x 6.58 --y -5.61 --geometry-um 130 --geometry-detail 0 --packing 0 --occupancy 0.5 --npc-dye None # 26 ms [served]
insiliscope_cli --geometry-json packing1.geometry.json --x 6.58 --y -5.61 --geometry-um 130 --geometry-detail 0 --occupancy 0.5 --npc-dye None # 566 ms [served]
Bit-exactness rules¶
The core was ported bit for bit from the original JS prototype (the reference implementation until 2026-10-28), and its outputs are frozen in the golden vectors; the rules that kept it bit-exact keep those outputs fixed:
isc::jsm::sin/cos/asin/atan/atan2/exp/log/cbrt/hypotare V8's fdlibm routines (generated, never hand-edited); the C++ standard library's transcendental functions are not used for anything the JS computes withMath.*.- Types follow the JS:
doublewhere JS has numbers,floatexactly where JS usesFloat32Array; operand order is kept. - No fast-math, no FMA contraction.
- JS
Math.powis the host'sstd::powin current V8 and cannot be matched across libms: the cytoplasm mesh is bit-exact only on the libm Node was built with, and "near" elsewhere (accepted). - Golden vectors (
spec/golden) were frozen from the prototype and are checked in CI (native and WASM).
The C ABI¶
Consumers see only core/include/insiliscope/insiliscope.h: extern "C", flat buffers, opaque handles, no exceptions or
STL across it. Queries return cells, assets (cell geometry and microtubules), dye sites, dye events in a time window
(isc_events_in_window), z-resolved dye density (isc_density3d_in_window, used by widefield) and the optical volume
(isc_optical_volume_in_window: cytoplasm, nucleus and microtubule volume fractions per voxel, used by brightfield).