Deterministic module ownership, runtime flow, and separation between physics, UI, and rendering
Spinning Magnets Lab is structured as a scientific application rather than a monolithic graphics demo. Its architecture is built around one central promise: physics is computed through a deterministic pipeline, and every major stage has a clear owner. This is what allows the simulator to remain auditable as the project grows.
At a system level, the simulator follows the chain below:
state.project → normalized project configuration → geometry placement → magnetic field evaluation → coil sampling and flux computation → induction runtime → derived outputs and diagnostics → UI panels and rendering
This runtime direction is deliberately one-way. Project state feeds the engine; the engine feeds the displays. The display layer is not allowed to repair schema, invent field values, or alter induction logic.
| Layer | Primary responsibility |
|---|---|
| Project / schema | Own defaults, normalization, and import-export compatibility |
| Geometry | Place magnets and coils in world coordinates |
| Field solving | Compute magnetic flux density from the active solver |
| Derived physics | Build measurement-oriented derived outputs from the current state |
| Induction runtime | Track linkage history, voltage, RMS, and peak metrics |
| Worker | Perform heavy deterministic calculations off the main UI thread |
| UI / rendering | Display existing results without changing the physics |
The project state is the authoritative machine description. It contains disc parameters, magnet parameters, coil configuration, field settings, simulation settings, and probe information. The defaults live in the canonical default-project definition, while schema ownership belongs to the project schema file.
The architectural intent is that every project entry path must normalize data before the rest of the simulator consumes it. This keeps file imports, presets, and future format revisions stable and scientifically interpretable.
Project import and export are handled in the project I/O layer, which sanitizes incoming JSON, merges it with defaults, and ensures that imported machine states regain a valid internal structure before they are applied.
The main application file is the orchestration layer. It wires together controls, worker messaging, simulation stepping, scope buffering, toolbar behavior, overlay toggles, and rendering updates. It must coordinate the system without becoming the owner of magnetic field mathematics.
In practice, the main runtime loop advances rotor angle, schedules derived builds, applies authoritative induction outputs to the latest derived object, and updates panels and canvases from the resulting state.
Coordinates controls, runtime mode switching, worker scheduling, and panel updates without owning field equations.
Offloads heavy derived calculations and overlay generation so the interface remains responsive while outputs stay deterministic.
Draws stage geometry, heatmaps, vectors, and flux lines from already-computed data only.
Geometry modules translate the abstract project description into placed magnets and placed coils. Once this spatial structure exists, the field layer becomes responsible for evaluating the magnetic field generated by the placed magnets. This separation is essential: geometry decides where things are, while the field layer decides what magnetic field they produce.
The derived engine then consumes those placed objects and field contexts to build instrument-oriented outputs such as heatmap overlays, vector overlays, line scans, field probes, flux lines, and rotor-related diagnostics.
One of the strongest architectural decisions in the project is the separation between general derived data and authoritative induction. The derived path assembles placed geometry, overlays, and diagnostics, while the induction runtime owns the evolving coil linkage history and voltage metrics.
The induction runtime maintains sampling caches for coil surfaces, computes flux from those cached sample locations, tracks recent linkage history, and derives instantaneous voltage, RMS voltage, and peak values. Those results are then injected back into the latest derived object so the UI can display them consistently.
This matters because it prevents the renderer or panel code from inventing physics values on demand. Instead, the measurement-style outputs come from one authoritative runtime path.
Heavy derived computations are performed inside a dedicated physics worker. The worker receives a build request, calls the derived builder, caches overlay products when appropriate, and posts the result back to the main thread through a defined message protocol.
This allows the application to remain interactive even when heatmaps, vector overlays, or flux-line calculations are active. Just as importantly, it preserves the separation between orchestration on the main thread and deterministic numerical work in the worker path.
The documentation shell, control panels, coil table, scope panel, probe displays, and stage rendering all belong to the presentation side of the project. These components may format numbers, show diagnostics, and provide interaction, but they must not repair missing schema or compute missing physics.
The current interface reflects this instrument-oriented layout: a left control area, central stage, and right-side measurement panels built around coil prediction, scope, rotor state, field probe information, and performance readout.
The intended dependency direction is simple: UI and rendering sit above orchestration, orchestration depends on application and engine modules, and the lower layers handle field, geometry, and utility logic. Forbidden crossovers include engine modules importing UI code, field modules depending on rendering, workers owning UI concerns, or UI-side schema repairs.
The architecture is designed to support growth without losing scientific clarity. That means new visualization layers can be added, new field solvers can be compared, and new instrument panels can be introduced, provided the one-way deterministic pipeline remains intact.
The simulator is therefore best understood as a structured instrument platform: project state defines the machine, geometry places the machine, field solvers compute the magnetic space, coil integration samples that space, induction runtime turns flux history into electrical observables, and rendering presents the result.