Blocks: 0 Wires: 0 Time: 0.000s Ready © 2026 SysDynLab — Okan Arpacik. All rights reserved.
Block Parameters ✕
Plotter ✕
SysDynLab Help ✕

SysDynLab Help

SysDynLab simulates how things change over time — a heating room, a motor, a pendulum, a control loop — by connecting blocks with wires. Everything runs in your browser; nothing is uploaded.

Finding things quickly

  • Search (at the top of this window) looks through every page and every block's help. Try branch, algebraic loop or PID.
  • Help on one block: right-click it on the canvas → ⓘ Block Help, or open Block Reference.
  • F1 opens this window from anywhere; Esc closes it.
  • Long pages list their sections at the top (On this page); every page ends with links to the previous and next one.
New here? Open the Help tab → Demos, pick any example, press ▶ Run in the Simulation tab, then double-click a Plotter block. Then read Quick Start.

The window at a glance

AreaWhat it is for
Top barSection tabs (File, Edit, View, Simulation, Code, Help) and, on the right, quick Undo, Redo and Save.
RibbonThe open tab's buttons, in labelled groups, just under the top bar.
Block library (left)Every block, by section, with a search box at the top.
Canvas (centre)Your diagram. Inside a subsystem a path bar (breadcrumb) at the top shows where you are; click a name to go back up.
Status bar (bottom)Block and wire counts, simulated time while running, messages and warnings, and Feedback.

Toolbar tabs

TabButtons
FileNew (empty model — asks first), Open, Save
EditUndo, Redo · Cut, Copy, Paste, Duplicate · Select all, Delete, Subsystem (group the selected blocks)
ViewZoom in, Zoom out, Fit (whole model on screen) · Library (show / hide the block library), Dark / Light
SimulationMethod (solver), Step dt, Stop time · Run (click again to stop; the tab shows a red dot while running)
CodeC or Python from the Whole model or the Selected subsystem
HelpThis window, Quick start, Blocks (Block Reference), Shortcuts · Demos (example models) · Feedback, Website

More room for the canvas: click the open tab again (or the arrow at the right end of the ribbon) to fold the ribbon away. Then clicking a tab opens its buttons only until you use one, click elsewhere or press Esc. Click the arrow again to keep it open. Your tab and this choice are remembered.

Block library

  • Search: type part of a name (pid, delay, park); Esc clears it.
  • Sections fold and unfold when you click their heading: Sources, Continuous, Math, Discrete, Motor Control, Nonlinear, Logic, Signal Routing, Sinks.
  • Motor Control holds Clarke, Park, Inverse Park, Inverse Clarke, SVPWM and an average Inverter as ready-built subsystems (double-click one on the canvas to see and change the math), the motor models DC Motor, PMSM and BLDC Motor (double-click for their parameters), and a PI controller with output limits and anti-windup.
  • Add a block: drag it onto the canvas, or click it to place it in the middle of the view.
  • Phones and small windows: the library slides in from the left — tap ☰ at the top left; press and hold a block, then drag it out.

Saving, opening and examples

  • Save (Ctrl+S) writes the whole model — blocks, wires and their routes, block names, the method, step and stop time — to an .xml file. Chrome and Edge ask where to save; other browsers download it.
  • Open (Ctrl+O) loads such a file and replaces the model on the canvas. New clears the canvas (it asks first).
  • Nothing is saved automatically and nothing leaves your computer: save before closing the page.
  • Demos (in the Help tab) loads a ready-made example; it replaces the current model, so save your own work first.
  • Measured data comes in through a CSV Source block and results go out through a CSV Sink — see Plots & Data.

Status bar

Messages appear here: what the simulator is doing, and warnings that need your attention (shown in red, and kept until the next action). See Troubleshooting for what each one means.

Adding blocks

  • Drag a block from the library onto the canvas, or click it to place it in the middle of the view.
  • Drop a block with one input and one output onto an existing wire to insert it there.
  • Paste copies with Ctrl+V; Ctrl+D duplicates the selection.

Changing a block's settings

  • Double-click the block. Edit, then click Apply or press Enter.
  • Number fields accept expressions: 2*pi, 1/0.05, sqrt(2)/2, 1e-3.
  • A Constant can hold a vector or matrix: 1,2,3 or 1,2;3,4 (see Vectors & Matrices).
  • Rename a block from its right-click menu; the name is shown under it, saved with the model and used in generated code.

Selecting and arranging

ToDo
Select several blocksDrag a box on empty canvas, or Shift+click; Ctrl+A selects all on this level
MoveDrag (wires follow), or nudge with the arrow keys (Shift = 1 px)
Rotate / mirrorRight-click → Rotate 90° / Mirror, so wires run the way you want
ResizeDrag the grip at the block's bottom-right corner (it shows when you point at the block)
Pan / zoomMiddle-button drag or Alt+drag / mouse wheel; Fit shows the whole model
DeleteSelect, then Delete or Backspace

Drawing wires

The small arrows on a block's sides are its ports: pointing in = input, pointing out = output. An output can feed any number of inputs; an input takes one wire.

ToDo
Connect two blocksDrag from an output to an input — or release anywhere on the target block and the nearest free input is used. Or click one port, then the other.
Choose the routeRelease the wire on empty canvas, then click at each corner; finish on the target port. Backspace removes the last corner.
CancelEsc or right-click
Branch a signalHold Ctrl (Cmd on Mac), press any point of a wire and drag to the new input (or release, then click the input). A dot marks the branch point.
Feed an input from a wireDrag from a free input and release on the wire
Re-plug a wireDrag a connected input port to another input; release on nothing to put it back
Tidy a wireDrag any straight piece of it sideways
Select / delete a wireClick it (Shift+click for several), then Delete
Many wires crossing? Send a signal with a Tag Send and pick it up anywhere on the same level with a Tag Read of the same tag — see Subsystems & Tags.

Right-click menu of a block

ItemWhat it does
Edit PropertiesOpen the settings (same as double-click)
RenameChange the name shown under the block
Rotate 90° / MirrorTurn or flip the block
Duplicate / Copy / Cut / PasteSame as Ctrl+D / Ctrl+C / Ctrl+X / Ctrl+V
Create SubsystemGroup the selected blocks into a new subsystem
ⓘ Block HelpHow the block works, its ports, an example and every setting
DeleteRemove the block and its wires
Subsystem: Enter / Settings / Dissolve / Generate CodeGo inside, add an enable port, ungroup, or export it as C / Python
Plotter: Set Number of InputsHow many plot rows it has

What a subsystem is

A subsystem is a block that contains other blocks — a box you can open. Use it to keep a large model readable (a "Plant", a "Controller", a "Sensor"), to reuse a part, or to generate code from just that part.

Making one

  1. Select the blocks to group (drag a box, or Shift+click).
  2. Right-click one of them → Create Subsystem (or Edit tab → Subsystem).

Wires that crossed the boundary are kept: inside, they now start at a Port In or end at a Port Out, and the subsystem block gets one pin for each. Dissolve (right-click) puts the blocks back on the parent level.

Working inside

ToDo
Go insideDouble-click the subsystem (or right-click → Enter)
Come back outEsc, or click a name in the path bar at the top of the canvas
Add a pinInside, add a Port In (input pin) or Port Out (output pin). Their port number sets the pin order, top to bottom
RenameRight-click → Rename; the name is shown on the block and in the path bar

Enabled subsystems

Right-click → Subsystem Settings… → Enable port adds a control pin on top. The subsystem runs only while that signal is non-zero. When it stops, choose what happens:

  • Reset — its states and outputs go back to their initial values;
  • Hold — it keeps its last states and outputs until it runs again.

Tag Send and Tag Read

A Tag Send publishes the signal wired into it under a name (its tag); every Tag Read with the same tag outputs that signal. They act like an invisible wire and are the easiest way to avoid long or crossing wires.

  • Tags work on one level: a Tag Read inside a subsystem only sees Tag Sends inside the same subsystem. To bring a signal in or out, use the subsystem's ports.
  • Each tag should have exactly one Tag Send.

Ready-built subsystems

The Motor Control section of the library drops complete subsystems built from basic blocks: Clarke, Park, Inverse Park, Inverse Clarke, SVPWM and an average Inverter. Inside, every signal is published with a Tag Send under its name (alpha, cos, offset…) and read next to where it is used, so the blocks read like the equations. Their pins are named on the block. Change anything you like — it is an ordinary subsystem.

Motor models

DC Motor, PMSM and BLDC Motor are subsystems with parameters. Double-click one to set them (resistance, inductances, flux or back-EMF constant, pole pairs, inertia, friction; the PMSM also offers d-q or three-phase terminals). Apply rebuilds the inside with the new values; right-click → Enter Subsystem shows it. Each one tells the automatic step size its electrical time constant, so with Step dt left empty the run stays accurate. The equations are in Block Reference under Motor Control.

Warnings in the status bar

MessageWhat it meansWhat to do
Algebraic loop detected: A → B → …A feedback loop with no memory block in it: each block needs the others' output of the same instant.Put a Unit Delay, Integrator, filter or other memory block in the loop (see How It Works).
Simulation diverged at "…"A value became infinite or not-a-number. Either the system really is unstable, or the step is too large for it.Check signs and gains (a positive feedback loop grows forever). Otherwise set a smaller Step dt or a stiff method (ESDIRK23, Implicit Euler).
Results may be inaccurate — ran out of solver sub-steps / did not convergeThe method struggled: the model is stiff (very fast and slow parts together) or switches sharply.Smaller Step dt, or a stiff method. For models with sharp switching or quantisation, a fixed-step method (RK4) is often best.
"…" has Ts … not a multiple of dtA discrete block's sample time does not line up with the simulation step, so it samples late.Set Step dt to Ts or an exact divisor of it (Ts = 0.01 → dt = 0.01, 0.005, 0.001…).
Shape mismatchA vector or matrix went into a block that takes a single value, or two sizes do not fit together.Split it with a Demux, or check the sizes (see Vectors & Matrices).
exceeds the engine limit of signal slotsA block has too many inputs or too wide a signal.Use fewer inputs per block or narrower vectors.
State machine errors (red, before the run)A chart has a mistake: an unknown name, an incomplete condition, a loop of junctions.Open the chart; its status bar names the state or transition. Unknown names can be added as inputs, outputs or local data from there.
Expression errorsThe Expression block found an unknown name or a typo.Use u (one input) or u1, u2… and functions such as sin, pow(a,b); ^ is not power.

Common problems

ProblemTry
The plot is emptyRun first, then double-click the Plotter. Check that the wire really ends on the Plotter's input (its arrow disappears when connected).
A signal is always 0An input left unconnected reads 0. Look for open input arrows; check Tag Send / Tag Read tags match and are on the same level.
The curve is jagged or steps are visibleMake Step dt smaller, or leave it empty so it is chosen for you.
The run takes longShorten the Stop time or enlarge Step dt; very small dt over a long time means millions of steps.
Results differ from the generated codeSimulate with a fixed-step method (RK4, Heun or Euler) and the same dt as the code — see Code Generation.
Can't see the block libraryView → Library, or on a phone tap ☰.
Lost somethingCtrl+Z undoes step by step. Models are not saved automatically — save with Ctrl+S.

Still stuck? Use Feedback in the status bar to report it — include what you did and the message you saw.

What is SysDynLab?

SysDynLab simulates how things change over time — a heating room, a motor, a pendulum, a control loop — by connecting blocks with wires, the same way engineers sketch systems on paper. It runs entirely in your browser; nothing is installed and nothing is uploaded.

Each block does one simple job (add, multiply, integrate, delay, plot…). Wires carry numbers from one block to the next. When you press Run, SysDynLab steps time forward from 0 to the Stop time and every block does its job at every step.

  • Over 50 blocks: sources, continuous dynamics, digital (discrete) blocks, math, logic, lookup tables, matrices
  • Subsystems to keep large models tidy, and state machines for modes and decisions
  • Solvers chosen automatically (you can override), live plots, CSV import/export, C/Python code generation
New here? Open the Help tab, click Demos and pick any example, press ▶ Run, then double-click a Plotter block. Then read How It Works — five minutes there makes every block make sense.

New to the window? The Interface explains every tab and button.

Building a Model in 5 Steps

1
Add blocks

Click a block in the left library to place it in the middle of the canvas, or drag it to where you want it. Right-click a block and choose ⓘ Block Help to see what it does, with an example.

2
Connect them

The small arrows on a block's sides are its ports: arrows pointing into the block are inputs, arrows pointing out are outputs. Drag from an output to an input to draw a wire (or click one, then the other). You don't have to hit the port exactly: releasing near it, or anywhere on the target block, snaps to the nearest free input. A port's arrow disappears once it is connected.

Split a signal (send it to a second block): hold Ctrl (Cmd on Mac), press on any point of an existing wire, and drag to the new input. A plain click just selects the wire.
Insert a block into a wire: drop an unconnected block onto the wire.
Choose the route yourself: release the wire on empty canvas, then click at each corner; finish on the target port. Backspace removes the last corner, Esc cancels.
Tidy a wire: drag any straight piece of it sideways.

3
Set parameters

Double-click a block to edit its settings, then click Apply (or press Enter). Number fields accept expressions such as 2*pi or 1/0.05.

4
Run

Set the Stop time (how many seconds to simulate) in the Simulation tab and press ▶ Run in the same tab. Leave the method on Auto and Step dt empty: SysDynLab picks both from your model. The button turns red while running; press it again to stop early. Warnings and errors appear in the status bar at the bottom.

5
Look at the results

Double-click a Plotter block to open its graph. Drag to pan, scroll to zoom, hover to read values. Fit shows everything again. A Display block shows a live number right on the canvas.

Your First Simulation: a Heating Object

A cup of tea on a hot plate doesn't heat instantly — it approaches the plate's temperature gradually. That behaviour is a first-order system, and you can build it in a minute:

  1. Add a Step (Sources), a Transfer Fn (Continuous) and a Plotter (Sinks).
  2. Wire Step → Transfer Fn → Plotter.
  3. Leave the Transfer Fn at its default num = 1, den = 1 1, which means 1/(s+1).
  4. In the Simulation tab set Stop time = 8 and press ▶ Run, then double-click the Plotter.

What you see: at t = 1 the Step switches on, and the output rises towards 1 — fast at first, then slower. One second after the switch (one time constant) it has covered 63 % of the way; after about four seconds it has practically arrived. Change the denominator to 2 1 (a time constant of 2 s) and run again: the rise is twice as slow.

Next step: add a controller. Put a Sum (signs +-) and a PID in front of the Transfer Fn, and branch the output back to the Sum's minus input. That is a feedback loop — see How It Works for why it works.

Everyday Tasks

TaskHow
Select several blocksDrag a box around them on empty canvas, or Shift+click
Move blocksDrag them (wires follow), or nudge with the arrow keys (Shift = 1 px steps)
Rotate / mirror / renameRight-click the block
DeleteSelect, then Delete or Backspace
Undo / redoCtrl+Z / Ctrl+Y
Copy & pasteCtrl+C, Ctrl+V — wires between the copied blocks come along, routes included
Pan / zoom the canvasMiddle-button drag or Alt+drag / mouse wheel. Fit shows the whole model
Group blocks into a subsystemSelect them → right-click → Create Subsystem. Double-click to go inside, Esc to come back out
Save / open a modelCtrl+S / Ctrl+O (XML file). Models are not saved automatically
Export resultsWire signals into a CSV Sink; the file downloads when the run ends
Generate C / Python codeCode tab → Whole model or Selected subsystem (also right-click a Subsystem → Generate Code). For a State Machine alone: open it → Generate C

The Big Idea

A block diagram is a recipe that is repeated over and over. SysDynLab chops time into small steps of length dt (for example 0.01 s). At every step, starting at t = 0 and ending at the Stop time, it:

  1. asks every block for its output, in the order the wires dictate (a block's inputs are always computed before the block itself);
  2. records what the Plotters, Displays and CSV Sinks receive;
  3. moves time forward by dt and updates everything that remembers (integrators, filters, delays…).

A wire always carries one number at each moment (or a list of numbers — see Vectors). A Plotter simply draws that number against time.

Two Kinds of Blocks: Instant and Memory

This is the single most useful idea for understanding any diagram. In Block Help every block with inputs and outputs is tagged with one of these two badges:

KindWhat it meansExamples
InstantThe output depends only on the input right now. Change the input and the output changes in the same step. Like a calculator.Gain, Sum, Product, Saturation, Switch, Compare, Lookup Table, Expression
Has memoryThe output also depends on what happened before. These blocks give a model its behaviour over time: rising, settling, oscillating, delaying.Integrator, Transfer Fn, Low-Pass, PID, Delay, Sample Delay, Discrete TF, Rate Limiter, State Machine

Sources (Step, Sine, Constant…) have no inputs — they produce signals from the clock. Sinks (Plotter, Display, CSV Sink) have no outputs — they only watch.

The Integrator Is the Heart of Dynamics

Almost every physical law says how fast something changes: velocity is how fast position changes, current is how fast charge changes, heat flow is how fast temperature changes. An Integrator turns a rate of change back into the quantity:

rate of change → [ Integrator ] → quantity

Feed it a constant 2 and its output climbs by 2 every second. Feed it 0 and it holds still. Its ic parameter is where it starts.

Turning an Equation into a Diagram

Take a mass on a spring with a damper, pushed by a force F:

m·x″ = F − c·x′ − k·x

The recipe, which works for any such equation:

  1. Solve for the highest derivative: x″ = (F − c·x′ − k·x) / m.
  2. Chain one Integrator per derivative: x″ → Integrator → x′ → Integrator → x. Now x′ and x exist as wires.
  3. Build the right-hand side from those wires: Gain c on x′, Gain k on x, a Sum with signs +-- for F − c·x′ − k·x, and a Gain 1/m.
  4. Close the loop: wire that result into the first Integrator's input. Set the initial position and velocity in the Integrators' ic.
Σ1/mIntegratorIntegrator Gain cGain k Fx″x′x +−−

Run it with a Step on F and a Plotter on x and you get the classic damped oscillation. Try the Van der Pol demo for the same pattern with a nonlinear twist.

Feedback Loops — and Why They Need Memory

A feedback loop is a path of wires that comes back to where it started, like the one above or a controller watching its own output. Loops are normal and are what control is all about — but every loop must pass through a block with memory, such as an Integrator.

Why: in a loop made only of Instant blocks, each block needs the other's output in the same step before it can compute its own — a chicken-and-egg problem called an algebraic loop. SysDynLab detects it and warns in the status bar, naming the blocks. Blocks tagged Breaks feedback loops in Block Help (Integrator, Low-Pass, Delay, Sample Delay, …) solve it, because their output this step only depends on the past.

Example: Setpoint → Sum (+-) → PID → Transfer Fn → output, with the output branched back to the Sum's minus input. The Transfer Fn carries memory, so the loop is fine.

Continuous vs. Discrete (Digital)

Continuous blocksDiscrete blocks
ModelThe physical world: things that change smoothlyDigital electronics and software that act at clock ticks
BlocksIntegrator, Transfer Fn, PID, Low/High-Pass, DelaySample Delay, Zero-Order Hold, Discrete TF, Discrete PID, Disc. Integrator
UpdatesEvery step, integrated by the solverOnce per sample period Ts; the output is held constant in between (a staircase)

A typical embedded-control model mixes both: a continuous plant (motor, room) measured through a Zero-Order Hold and controlled by a Discrete PID with the same Ts — exactly like a microcontroller. For accurate sampling, choose a dt that divides Ts evenly (Ts = 0.01 → dt = 0.001).

Step dt, Stop Time and the Solver in One Paragraph

In the Simulation tab, Stop time is how long to simulate. Step dt is the step between recorded points: smaller is more accurate and smoother but slower. The solver is the method used to advance the memory blocks from one step to the next. Leave Auto and an empty dt, and SysDynLab picks them from the fastest thing in your model, showing its reasoning in the status bar. If a run "diverges" (numbers blow up), the status bar names the block where it happened — see the Solver Guide.

Signals Can Be Lists: Vectors

A wire can carry several numbers at once (a vector) — for example the three coordinates of a position. Vector wires are drawn thicker. Bundle signals with Mux, split them with Demux. Most math blocks work element by element, and a Mux into a Plotter draws one coloured line per element. Details are in the Vectors & Matrices tab.

Reading Someone Else's Diagram

  1. Find the sources (left side, no inputs) — they are what drives the system.
  2. Find the memory blocks — they hold the system's state (position, temperature, charge…). Their number is the order of the system.
  3. Follow each loop back and read its Sum signs: a minus sign in the loop means negative feedback (stabilising, like a thermostat).
  4. Right-click any unfamiliar block → ⓘ Block Help.

Blocks are listed in the same groups as the library. Click a block to see how it works, an example and its parameters. Badges: Instant output depends only on the input now · Has memory output also depends on the past · Breaks feedback loops can close a loop on its own (see How It Works).

Loading block reference…

Plotter Block (Scope)

The Plotter block records signals during simulation and lets you view them as time-series plots. It is the primary data visualization tool in SysDynLab.

Setting Up a Plotter

  1. Drag Plotter from the Sinks section of the sidebar onto the canvas.
  2. Double-click the block to set nInputs — the number of signal channels (1–10).
  3. Wire signals from other blocks into the input ports.
  4. Press ▶ Run.
  5. Double-click the Plotter block again to open the signal viewer.

Viewer Toolbar Controls

Button / ActionWhat it does
Zoom XActivate X-axis (time) zoom mode. Click once for the start point, click again for the end point — the time axis zooms to that range. Blue band previews the selection.
Zoom YActivate Y-axis (value) zoom mode. Same two-click selection, but locks the value axis.
↔+ / ↔−Zoom in / out horizontally (time axis) by a fixed factor.
↕+ / ↕−Zoom in / out vertically (value axis) by a fixed factor.
FitReturn to the full auto-scaled view (all data visible).
NamesToggle the channel-name panel. Type a custom label for each channel. Names appear on the plot legend and in cursor readouts, and are saved with the model.

Mouse Interaction on the Plot

ActionHow
Pan left / right / up / downClick and drag on the canvas
Zoom in / outScroll wheel anywhere on the canvas
Read cursor valuesMove the mouse over the canvas — a vertical cursor snaps to the nearest data point and shows time + value for every channel

Channel Names

Click Names in the viewer toolbar. A row of text boxes appears below the toolbar — one per physical input port. Type a descriptive label such as Speed or Error. The name appears:

  • In the inline legend on the right side of the plot
  • In the hover cursor readout (Speed = 3.142)
  • As the row label when subplot mode is active

Names are stored in the block parameters and saved with the model XML.

Mux Input → Overlay Mode

If the Plotter has nInputs = 1 and the single port is wired from a Mux block, SysDynLab automatically expands the vector and draws all elements as separate colored traces on one shared plot. This is the easiest way to compare several signals side-by-side without adding multiple Plotter blocks.

Example: Mux 3 signals (reference, output, error) → single Plotter input → three colored lines in the same plot.

Multi-Input → Subplot Mode

If the Plotter has nInputs > 1, each physical input port gets its own subplot row. All rows share the same time axis so you can compare timing across signals. Each row auto-scales its Y axis independently.

If a port in subplot mode is itself wired from a Mux, that row shows all Mux elements overlaid within its subplot — giving you both subplot isolation and overlay comparison simultaneously.

Typical Patterns

GoalSetup
Compare 3 signals on one plotMux 3 → Plotter (nInputs = 1)
Compare 3 signals each on its own rowPlotter nInputs = 3, one signal per port
Overlay a vector + show another signal separatelyPlotter nInputs = 2; port 1 ← Mux(3); port 2 ← scalar signal
Name channelsOpen viewer → click Names → type labels
Zoom into transient at t=5sClick Zoom X → click at t=4.8 → click at t=5.5

XY Plot Block

The XY Plot block plots one signal against another in phase-plane style — there is no time axis. Input 1 maps to the X axis; Input 2 maps to the Y axis. The result is a parametric trajectory curve.

Common uses: visualizing limit cycles, attractors, Lissajous figures, and velocity vs. position phase portraits.

Example: Connect a Sine Wave to Input 1 and a Cosine (Sine with phase = π/2) to Input 2 to draw a circle.

Display Block

The Display block shows the current numeric value of its input in a green LCD-style readout — updated live at every simulation step. Use it to monitor a single value without opening a scope.

  • If the input is a scalar, one row is shown.
  • If the input is a vector (from a Mux), each element gets its own row. The block height grows automatically.
  • Values use automatic precision: small/large values are shown in scientific notation.
  • The display also shows the last value frozen at the end of simulation, useful for checking final steady-state.

CSV Sink

The CSV Sink records all input channels during simulation and saves them as a comma-separated file when the simulation ends. The filename is the block label.

  • Set nInputs to the number of channels to record.
  • Connect a Mux to record multiple signals as separate columns.
  • The file has a time column followed by one column per channel.
  • Use this to post-process data in Excel, Python (pandas), or any other analysis tool.

What Is the State Machine Block?

The State Machine block implements statecharts with hierarchical states, parallel (AND) regions, junctions, entry/exit actions, transition conditions, local variables, and arbitrary input/output ports. The entire statechart runs inside the WASM simulation engine at each time step.

Adding a State Machine

  1. Drag State Machine from the Logic section of the library.
  2. Double-click the block to open the chart editor.
  3. With nothing selected, the right-hand Symbols panel lists the inputs, outputs and local data. Add and rename them there.
  4. Double-click the canvas to add states; drag from a state's edge to draw transitions.
  5. Click Done (or press Esc) and wire the block like any other.

Editor Layout

AreaPurpose
Tool strip (left)State (S) and Junction (J). A tool places one element and then goes back to normal editing; Esc or a second click on it cancels.
CanvasThe chart. Superstates are drawn with their states inside them. Drag on empty space to select several states; pan with Space-drag, middle-drag or Alt-drag; the wheel zooms, F fits the chart.
Inspector (right)Fields for the selected state or transition; with nothing selected, the Symbols (inputs, outputs, local data). Every change applies immediately.
Status barProblems in the chart (click to jump to the state or transition), and hints.
? buttonA short cheat sheet of gestures, label syntax and keys (also ? or F1).

Every edit can be undone with Ctrl+Z (redo Ctrl+Shift+Z). Right-click anything for its commands.

States

Only one state is active at a time (per parallel region). A state's label is its text, written on the state:

Heating
entry: count = 0
out: heater = 1; fan = 1
exit: count = count + 1
LineMeaning
first lineThe state's name.
entry:Actions run once when the state is entered.
out:Constant output values written every step while the state is active (output = number, separated by ;). Use entry: for anything computed.
exit:Actions run once when the state is left.
OperationHow
Add a stateDouble-click the canvas (you can type the label straight away), or the State tool, or right-click → Add state here
Edit the labelDouble-click the state (finish with Ctrl+Enter or click away, Esc cancels), or use the inspector fields
Move / resizeDrag the state; select it and drag any of the handles on its corners and sides. Arrow keys nudge.
Several statesDrag a box around them, Shift-click to add or remove, Ctrl+A for all. Drag one to move them together; the inspector aligns, spaces or sizes them.
Default stateThe state entered first is marked by a dot and arrow. Right-click → Set as default, or the inspector checkbox. Drag the dot to move the arrow around the state.
Copy / paste / deleteCtrl+C / Ctrl+V (copies the inner states and their transitions too) / Delete

Superstates (hierarchy)

A state that contains other states is a superstate. Its inner states are active only while it is active.

  • Nest a state by placing it inside another (State tool click, or drag it in) — the outer state grows to fit. Enlarging a state around existing states also makes them its children.
  • A transition into a superstate enters its default inner state. A transition out of a superstate fires whichever inner state is active and leaves all of them (exit actions from the inside out).
  • Transitions join states with the same parent. A move or resize that would split a transition across two superstates is undone with a message.
  • Subchart: tick Subchart (or right-click → Make subchart) to show a superstate as a plain box; double-click it to open its contents, and use the breadcrumb or Backspace to go back up. Expand subchart in place draws them inside it again.

OR and AND Superstates

Every superstate is one of two kinds; a small OR / AND tag next to its name shows which:

  • OR (exclusive) — one inner state is active at a time, entered at its default state. This is the usual kind.
  • AND (parallel) — all inner states are active together and run every step in order.

These are hierarchy, not separate kinds of state: right-click a state, or several selected states, and choose Group into superstate (OR) or Group into parallel superstate (AND) (Ctrl+G / Ctrl+Shift+G). A state that already has states inside it is switched under Inner states in the inspector, or by right-click.

Parallel (AND) details

Select a superstate and choose Parallel (AND) in the inspector (or right-click). All of its inner states are then active together; they are drawn with dashed borders and a number, the order in which they run each step (change it with the inspector's Order). Parallel states have no default state and no transitions between them; they can read and write the same outputs and local data to coordinate.

Transitions

When a transition's guard is true, the chart leaves the source (exit actions), runs the transition action, and enters the target (entry actions). The label uses the UML notation event [guard] / action, every part optional:

LabelMeaning
[temp > 21]Fires when the guard is true
[rise(button)] / count += 1Guard, then an action run when it fires
/ y = 0 or emptyNo guard: fires on the next step
E [x > 1]Waits for event E (see Events below), then checks the guard
OperationHow
DrawDrag from the edge of a state (the pointer becomes a crosshair) to another state or junction. Release on empty space to create a new state there, already connected. Release back on the same state for a self-transition (also right-click → Add self-transition).
Edit the labelTyped right after drawing; later double-click the transition or its label. Enter finishes.
OrderWith several transitions from one state, each shows a number: they are checked in that order and the first true guard fires. Change it in the inspector.
ShapeSelect it, then drag the middle handle to bend it, an end to move where it attaches — drop it on another state or junction to reconnect the transition, or on empty canvas to leave that end unconnected (no need to select it first). An unconnected transition is drawn dotted with an open dot on its loose end, is listed under ⚑ checks, and is ignored by the simulation and the generated code until both ends are connected again — or the label to move the text. Reset shape undoes all three.

Events

An event is an input pin whose edge a transition waits for. In Symbols, click + Event (or ⋯ → Make it an event on an input) and choose rising, falling or either. Write it in front of the guard: E, E [guard], E [guard] / action. A transition with an event fires only in the step where that edge happens.

Chart runs (under Events) can be set to only on event E: then every transition out of a state that names no event of its own also waits for E, so the chart only changes state on that edge (a triggered chart). State timers (state_time) keep counting real time.

Data Types

Local data is real (any number) by default. Choose int or bool in its row:

  • int — every assignment drops the fraction (toward zero) and is limited to the 32-bit range; generated C declares int32_t.
  • bool — stores 1 for any non-zero value, otherwise 0; generated C declares unsigned char.
  • true and false can be used anywhere as 1 and 0.

int and bool data are single values (size 1).

Flowchart Patterns

Right-click a state and choose Add if / else if / else… or Add switch / case…. A dialog lists the branches: type each condition (or case value) and its action, use + else if / + case for more, and tick else / default for the fall-through action. Insert builds the flowchart: a vertical line of junctions; each condition branches right (order 1), its action runs down its own column (/ action), all columns join back on the line, and falling through (order 2) tries the next condition, then the else. From a state it runs every step the state is active, without leaving it (the paths end in a junction with no way out). Right-click empty canvas to Insert one unconnected and draw a transition to its top junction yourself.

Switch / case uses the standard switch layout: every case branches right to the same column, runs its action down, and joins a rail on the right that returns to the line at the bottom; the default action is on the fall-through.

Stand-alone flow chart: inserted on a level that has no states, the flowchart gets the default transition (dot) and runs every time the chart runs. The optional description in the dialog is shown as a // comment beside it (edit it later under the junction's Comment).

Extending a flowchart: right-click a vertical transition that ends in a junction — for example an action column — and choose Insert if / else if / else here… or Insert switch / case here…. The new pattern hangs from that junction and the rest of the flowchart moves down and right to make room; this builds nested ifs.

Chart Checks and Autocomplete

Next to the problems in the status bar, ⚑ checks lists things that are legal but probably not intended: states nothing leads to, junctions never reached, transitions that can never fire (an earlier one from the same state has no guard), guards without variables, states that are never left, outputs never set, local data read but never assigned, events no transition uses. Click a check to show it; the elements are outlined in amber.

While typing a label or an inspector field, matching inputs, outputs, local data, events, functions and constants are suggested: ↑↓ to choose, Enter or Tab to insert, Esc to dismiss. At the start of a state's line it offers entry:, out: and exit:.

Junctions

Junctions (small circles) are branch points, never active states. When a transition reaches one, the first of its outgoing transitions whose guard is true is followed.

UseHow it works
If / elseSeveral outgoing transitions with different guards pick the target.
Action onlyWhen no outgoing guard is true (or there are none), the actions along the path still run and the chart stays in the source state — a conditional action without a state change.
Default junctionMake a junction the default (right-click → Set as default) to choose the first state by guards when the chart starts.

Symbols — Inputs, Outputs & Local Data

Click empty canvas to show the Symbols panel:

SectionPurposeUsage in expressions
InputsSignals into the block, one port each (top port = first row).Read-only: speed > 10
OutputsSignals out of the block.Assign in actions or out:: mode = 2. Read in guards: mode == 1
Local dataValues kept between steps, not visible outside. They start at their initial value each run; a size above 1 makes an array (buf[i]).Read and assign: counter = counter + 1
  • The panel starts with a picture of the block: the numbers on its pins match the numbered rows.
  • The second column is the width (1 = scalar). Names in grey italics are not used anywhere yet.
  • The ⋯ menu on a row moves it up or down (reordering the block's pins), changes it between input, output and local data, or deletes it.
  • Wires stay connected to their signal. When you close the editor, a wire follows its port wherever it moved; a wire on a port you deleted (or turned into local data) is removed.
  • Renaming a symbol renames it in every label of the chart.
  • Use a name in a label before defining it and the panel offers Input / Output / Local buttons to add it (the likely choice is highlighted: assigned names are outputs, read names inputs). A new name on an out: line becomes an output straight away.

Expression Language

Conditions and actions share a small expression language. Both paths (WASM runtime and C code generator) support the same operators.

Operators

CategoryOperatorsExample
Arithmetic+   -   *   /speed * 0.1 + bias
Unary minus-x-error
Comparison>   >=   <   <=   ==   !=error >= threshold
Logic&&   ||   !a > 0 && b < 1
Bitwise&   |   ^   ~   <<   >>flags & 0x03
Modulo%step % 4 (uses fmod internally)
Assignment (action only)=   += -= *= /=   ++ --output = input + 1, count += 1, n++
Multi-statement (action only);y = 1; counter = 0

Built-in Functions

FunctionDescription
abs(x)Absolute value
sqrt(x)Square root
sin(x) cos(x) tan(x)Trigonometry (radians)
asin(x) acos(x) atan(x)Inverse trig (radians)
atan2(y, x)Four-quadrant arctangent
exp(x)ex
log(x)Base-10 logarithm
ln(x)Natural logarithm
pow(x, y)x raised to the power y
min(a, b)   max(a, b)Minimum / maximum of two values
floor(x)   ceil(x)   round(x)Rounding functions
sign(x)−1, 0, or +1 based on sign
rise(x)True in the step where input x goes from ≤ 0 to > 0. Input ports only.
fall(x)True in the step where input x goes from > 0 to ≤ 0. Input ports only.
state_timeSeconds since the current state was entered (for a transition: its source state). Use it for timers: state_time > 5.
pi   eMathematical constants π and e

Example Patterns

GoalConditionAction
Transition when error exceeds thresholdabs(error) > 0.5output = 1
Count steps in a state(empty — always fires from junction)counter = counter + 1
Reset counter on entry—Entry: counter = 0
Trigger on rising edgerise(trigger)output = 1; timer = 0
Stay in state for N stepscounter >= Ncounter = 0
Two-condition if/else via junctionsTrans A: x > 0   Trans B: x <= 0Route from junction to StateA or StateB

Running the Simulation

After closing the editor, the State Machine block looks like any other block — wire its input and output ports, set simulation time, and press Run. The statechart evaluates at every simulation step: reads inputs, checks transition conditions (in order), fires matching transitions, runs actions, and writes outputs.

  • Each step, every active state gets one chance to change: transitions of its enclosing superstates are tried first (outermost first), then its own, in their Order. The first whose condition is true fires. A state entered in this step waits until the next step.
  • Firing a transition leaves the source (its active inner states first, innermost first, each running its exit action), runs the transition's action and any junction actions, then enters the target (entry action, then its initial inner state).
  • Outputs hold their last value — whether it was written by an action or by a state's fixed output value. A state's fixed output values are written every step while it is active, after the actions of that step. Outputs start at 0.
  • Local variables start at their initial value at t=0 and persist for the entire simulation.
  • Parallel regions run side by side — each active region can change state in the same step.
  • Mistakes are reported, not ignored. A misspelled name, an unfinished condition, = where == was meant, or a chart over the size limits (64 states, 128 transitions, 16 locals) stops the run with a message naming the state or transition. The editor's status bar shows the same problems while you edit.
  • Use a Display block on an output port to watch the active state value live.
Tip: To inspect which state is active, add an output port named mode and assign a distinct integer in each state's entry action (e.g. mode = 1, mode = 2). Connect it to a Display block to monitor the active state during simulation.

Generating C Code from a State Machine

SysDynLab can export the State Machine block as self-contained C or Python code — the same logic that runs in the WASM simulation engine, translated to portable source code ready for embedded targets or desktop applications.

Double-click the State Machine block to open the editor and click ⚙ Generate C in its toolbar. To get Python, or C for a whole controller, put the chart in a Subsystem and use the Subsystem's Generate Code.

  • The generated code behaves exactly like the simulation, step for step: it is produced from the same compiled conditions and actions the simulator runs. (Checked by running charts in both and comparing every output at every step.)
  • Everything lives in a name_ctx_t struct — inputs, outputs, locals and which states are active — so you can run several instances; there are no global variables.
  • Call name_init(&ctx) once, then name_step(&ctx, dt, inputs…) every dt seconds and read the outputs from ctx, e.g. ctx.heater.
  • The editor produces a .c and a .h file; a Subsystem's Generate Code includes the chart in its own C or Python output.

What Are Vector & Matrix Signals?

Every wire in SysDynLab carries a signal with a shape (R, C) where R is rows and C is columns. A scalar is (1, 1), a row vector is (1, N), a column vector is (N, 1), and a matrix is (R, C) with both > 1. Hover any wire to see its shape in the tooltip; non-scalar wires also get a thicker stroke and a small badge.

Creating Vectors and Matrices

Use a Constant block with the matrix literal syntax in its value field:

  • 5 — scalar (1,1)
  • 1, 2, 3, 4 — row vector (1, 4) (comma separates columns)
  • 1; 2; 3; 4 — column vector (4, 1) (semicolon separates rows)
  • 1, 2; 3, 4 — matrix (2, 2)
  • [1, 2; 3, 4] — brackets are allowed but optional

Each cell may contain any expression supported by the numeric evaluator (pi, sin(0), 1e-3, etc.). Trailing commas or mismatched row lengths produce a parse error.

Broadcasting Rules

SysDynLab uses scalar-only broadcasting by design — a scalar (1, 1) broadcasts to any shape, but two non-scalar operands must have identical shapes. NumPy-style row/column broadcasting is deliberately not supported, since it hides numerical bugs in dynamics models.

Wire Visuals

  • Scalar wires render as 1 px lines.
  • Vector/matrix wires get a thicker stroke proportional to log₂(N) with a small [R×C] badge near the source port.
  • Hover any wire to read its full (R, C) shape in the tooltip.

Element-Wise Math Blocks

Most math blocks operate element-wise on vector signals automatically:

  • Gain, Abs, Sign, Saturation, Dead Zone, Relay — unary, output shape = input shape.
  • Sum, Product, Divide, Min/Max — element-wise; non-scalar operands must share the same shape; scalars broadcast.

Stateful blocks (integrator, lpf, hpf, pid, transferfn, delay, …) accept vector input and run independent per-element state. Initial conditions broadcast across channels.

Mux & Demux — Packing and Unpacking

  • Mux concatenates its inputs into a single vector. mux(a, b, c) with three scalars produces (1, 3). With mixed scalar + vector inputs, widths add.
  • Demux splits a vector input into N scalar outputs (one per element).

Matrix Operations Block

Drop a Matrix Op from the sidebar and pick the operation from the dropdown. The block automatically resizes its input/output ports based on the operation. Switching ops removes any wires that no longer have valid landing ports (a status message reports how many were removed).

OpInputsOutputsNotes
Transpose(R, C)(C, R)Row-major transpose.
Matrix Multiply(M, K), (K, N)(M, N)Standard A·B. Inner dimensions must match.
Trace(N, N)scalarSum of diagonal. Square only.
Dot Producttwo vectors of equal lengthscalarΣ ui vi.
Cross Producttwo 3-element vectors3-element vectorPreserves orientation.
Norm(R, C)scalarFrobenius (√Σa²), ∞-norm (max row sum), 1-norm (max col sum). Sub-dropdown appears for type.
Determinant(N, N)scalarLU decomposition + product of U diagonal × pivot sign.
Inverse(N, N)(N, N)LU + back-substitution per column. Singular → zero output (NaN sentinel collapses under -ffast-math).
Solve A·x = b(N, N), (N)(N)LU + back-substitution. Cheaper than computing inv(A)·b.
Cholesky(N, N)(N, N)Lower triangular L such that A = L·LT. Requires symmetric positive-definite input.
LU Decomposition(N, N)L (N,N), U (N,N), P (N,N)3 outputs. Partial pivoting. P·A = L·U.
QR Decomposition(N, N)Q (N,N), R (N,N)Householder reflections. Q orthogonal, R upper triangular.
Symmetric Eig(N, N)eigvals (N), eigvecs (N, N)Jacobi rotations. Symmetric matrices only. Each column of eigvecs is an eigenvector.

Storage Cap (N ≤ 11)

All matrix operations use stack-allocated buffers in the WASM engine. The current cap is N ≤ 11 per dimension (since 11×11 = 121 ≤ MAX_PORTS = 128). Inference rejects larger matrices with a clear error. This is plenty for typical control / Kalman / robotics use cases; larger matrices would need a dynamic-allocation refactor.

Input Validation Warnings

When the input matrix is a literal Constant block, SysDynLab statically validates SPD for chol and symmetry for eig. You will see banner errors like:

  • chol requires symmetric input — A[0,2]=7 ≠ A[2,0]=3
  • chol requires positive-definite input — leading minor #2 has non-positive pivot (-3.00e+0)
  • eig requires symmetric input — A[0,1]=2 ≠ A[1,0]=3

When the matrix is computed by upstream blocks, only shape and squareness are checked; failure produces an undefined output (typically zeros) instead of an error. Validate your input before relying on the result in those cases.

Displaying Vectors and Matrices

  • Display blocks auto-detect matrix shape and render an R×C grid with right-aligned, monospace-padded columns.
  • Plotter blocks expand vectors into multiple traces. A single Plotter port fed by a vector signal shows all elements overlaid; multiple ports get stacked subplots.
  • CSV Sink writes one column per scalar channel; vector signals are flattened row-major.

Code Generation Support

All 13 matrix operations are emitted by the C and Python code generators, using the simulator's own algorithms (LU with partial pivoting, Householder QR, Jacobi eigenvalues) as small helper functions, so the results match the simulation. No NumPy or LAPACK is needed.

Multi-output operations (lu, qr, eig) emit one signal variable per output port; downstream consumers reference them just like single-output blocks.

Common Patterns

Kalman Filter Update

Innovation covariance S = H·P·HT + R:

Constant(H) ─┐
               ├─ MatMul ─┐
Constant(P) ─┘             ├─ MatMul ─┐
Transpose(H) ──────────────┘          ├─ Sum ─ Display
Constant(R) ──────────────────────────┘

Solving a Linear System Every Step

If your dynamics need to solve A·x = b with A changing each step, use the Solve op directly — it’s ~3× cheaper than computing inv(A) and multiplying.

Eigendecomposition for Modal Analysis

For a symmetric stiffness matrix K:

Constant(K) ─ MatrixOp[eig] ─┬─ Display (eigenvalues = natural freq²)
                              └─ Display (eigenvectors = mode shapes)

Limitations

  • A signal carries at most 32 elements. Blocks with memory (Integrator, PID, filters, delays, discrete blocks) take at most 8 per block — Discrete PID 7. A wider signal is refused before the run, with a message naming the block.
  • N ≤ 11 per dimension for the matrix operations (stack-cap; lift requires dynamic allocation).
  • Symmetric eig only. General-matrix eigenvalues need the QR algorithm with shifts; a hand-rolled implementation would silently fail on edge cases. Treat as a future LAPACK-binding project.
  • Cholesky/eig validation is static only when the input traces directly to a Constant literal. Runtime input is not validated.
  • State-machine blocks remain scalar in expressions (SM array locals are vectors but not matrices).

Overview

SysDynLab turns a Subsystem into a self-contained C file (C99 or ANSI C89) or Python file. The C needs only <math.h>; the Python needs only the standard library. Every block in the library is supported, including vectors, matrix operations, state machines and enabled subsystems.

The generated step function does exactly what the simulator does in one time step: the same execution order, the same arithmetic, the same integration method. With the same dt and a fixed-step solver (Euler, Heun or RK4), generated code reproduces the simulation's results step for step — that is checked automatically for every block type.

How to Open the Code Generator

  1. Open the Code tab and click Whole model, or select a subsystem and click Selected subsystem. (Right-click a Subsystem → Generate Code does the same; a lone State Machine also has Generate C in its editor.)
  2. Choose the language and options in the left panel, then click ▶ Generate.
  3. Copy or ↓ Download the result.

Options

OptionNotes
IntegrationHow continuous blocks (Integrator, Transfer Fn, PID, filters) advance each step: Runge-Kutta 4, Heun or Euler — the simulator's fixed-step solvers. It is preset to the solver you simulate with. The variable-step and implicit solvers (Auto, RK45, ESDIRK23, Implicit Euler, Trapezoidal) have no fixed-step equivalent, so the generated code's results then differ slightly from the simulation; simulate with RK4 to compare exactly.
Function / Class NamePrefix of every generated name, e.g. motor_ctrl gives motor_ctrl_init, motor_ctrl_step, motor_ctrl_State (C) or class motor_ctrl (Python).
Data Type (C)double matches the simulation. float is for targets without a 64-bit FPU; results then differ in the last digits.
C StandardC99, or ANSI C89 for old compilers (no <stdint.h>, declarations only at block starts).
Optimization Level + GCC optimize attributeAdds __attribute__((optimize("-O2"))) to the step function. Only GCC uses it; other compilers ignore it.
Linkagestatic keeps the functions private to one .c file; extern exports them for use from other files.
const input pointerDeclares the inputs const real *u.
volatile stateDeclares the state volatile, for state shared between an interrupt and the main loop.
NaN / Inf guardReplaces a NaN or infinite output by 0 before returning it.
Generate header (.h)Appends the prototypes to paste into a header file.
Generate test main()Appends a main() that runs 1000 steps and prints the outputs: gcc -std=c99 model.c -lm && ./a.out.
Inline commentsLabels each block's code with its name.

Using the Generated C

The inputs are the subsystem's Port In blocks in port order (u[0], u[1], …) and the outputs its Port Out blocks (y[…]; a vector output takes several consecutive entries). Call _init once, then _step every dt seconds with t = k·dt:

static motor_ctrl_State st;
static double u[2], y[1];
static unsigned long k;

void control_init(void) { motor_ctrl_init(&st); }

void control_isr(void) {            /* every 1 ms */
    u[0] = read_setpoint();
    u[1] = read_speed();
    motor_ctrl_step(&st, k++ * 0.001, 0.001, u, y);
    write_pwm(y[0]);
}

A subsystem with no memory (only gains, sums, …) has no State and its step takes no state argument.

Using the Generated Python

m = motor_ctrl()
dt = 0.001
for k in range(5000):
    y = m.step([setpoint, speed], k * dt, dt)

What One Step Does

  1. Every block computes its output, in the simulator's execution order. Blocks with memory (Integrator, Sample Delay, filters…) output their stored state, which is what lets feedback loops work.
  2. Blocks that only read their input into memory (Sample Delay, Derivative, Transport Delay, forward Disc. Integrator) store this step's input.
  3. The continuous states are integrated over dt with the chosen method.

Subsystems are flattened into one function, Tag Send/Read pairs and Mux/Demux become direct references, and an Enabled Subsystem's blocks are wrapped in if (enable != 0) with its hold or reset behaviour. State machines become their own small _ctx_t struct with _init and _step functions — the same code the editor's Generate C produces.

Transport Delay buffers are sized for the simulation dt at generation time; running the code with a larger dt shortens the delay. Noise uses the simulator's generator; with Seed = 0 the simulation picks a random seed, the generated code uses 12345.

Algebraic Loops

A feedback loop with no block that has memory is an algebraic loop. The generated file then starts with a warning naming the blocks; as in the simulation, each of them reads the others' values from the previous step. Insert a Sample Delay, Integrator or filter to break the loop properly.

Solver Selection

Auto (the default, recommended) — SysDynLab looks at the time constants in your model (transfer-function poles, filter cutoffs, PID derivative filters…). If the slowest is more than 100× slower than the fastest, the model is stiff and Auto uses ESDIRK23; otherwise it uses RK45. With dt left empty it also picks the step size, and the status bar names the block that limited it.

SolverOrderTypeBest For
Euler1Fixed, Non-Stiff Quick previews, educational. Fast but least accurate — needs very small dt.
Heun (RK2)2Fixed, Non-Stiff Moderate accuracy at low cost. Good balance for simple systems.
RK44Fixed, Non-Stiff Excellent accuracy for most models. The workhorse fixed-step solver.
Implicit Euler1Fixed, Stiff Unconditionally stable for stiff systems but low accuracy. Use when stability matters more than precision.
Trapezoidal2Fixed, Stiff Good accuracy + stiff stability. Recommended fixed-step choice for stiff models.
RK45 Dormand-Prince5Variable, Non-Stiff Highest accuracy. Adapts step size internally for optimal performance. Best for smooth, non-stiff dynamics.
ESDIRK233Variable, Stiff Adaptive + stiff-capable. For models with wide time-constant spread (fast & slow modes together).

When Is a System “Stiff”?

A system is stiff when it has both very fast and very slow dynamics simultaneously. Examples:

  • A fast filter (e.g. 1 kHz) inside a loop whose other dynamics take seconds
  • A transfer function with widely separated poles (e.g. s + 0.1 and s + 1000)
  • A PID whose derivative filter (N) is much faster than the plant it controls

A single fast filter on its own is not stiff — stiffness needs a large ratio between time scales. Non-stiff solvers (Euler, Heun, RK4, RK45) require very small dt for stiff systems — they can blow up or oscillate. Stiff solvers (Implicit Euler, Trapezoidal, ESDIRK23) remain stable even with larger dt.

Step Size (dt) Guide

Leave dt empty (recommended) — SysDynLab auto-suggests dt from your model’s fastest dynamics (sine frequencies, filter cutoffs, transfer function poles).

SolverSamples per PeriodRule of Thumb
Euler~50dt < 1 / (50 × fmax)
Heun~30dt < 1 / (30 × fmax)
RK4~15dt < 1 / (15 × fmax)
Implicit Euler~15Stable at any dt, but accuracy needs ~15/period
Trapezoidal~10dt < 1 / (10 × fmax)
RK45~10Adapts automatically — dt sets the output resolution
ESDIRK23~7Adapts automatically — dt sets the output resolution

fmax = fastest frequency in your model (highest sine freq, filter cutoff, or natural frequency).

Examples

ModelRecommended SolverTypical dt
Step → 1st order TF (1/(s+1)) → ScopeRK40.01 – 0.05
Sine (10 Hz) → Gain → ScopeRK40.005
Step → PID → Plant with fast pole (1/(0.001s+1))Trapezoidal or ESDIRK230.0001 – 0.001
Chirp (0.1–100 Hz) → LP filter (50 Hz) → ScopeRK45auto (0.001)
Discrete PID + Sample Delay (no continuous blocks)Euler (any)Match sample time

Discrete Blocks

Discrete blocks (Sample Delay, Zero-Order Hold, Discrete TF, Discrete PID) update once every Ts seconds and hold their output in between. Ts = -1 means "every simulation step". For exact sampling, dt must divide Ts evenly (Ts = 0.01 → dt = 0.01, 0.005, 0.001…). If dt is larger than Ts, the block can only run every dt and SysDynLab warns you. Auto dt takes sample times into account. A purely discrete model works with any solver. (Disc. Integrator and Disc. Derivative currently update every step: keep their Ts at -1.)

Troubleshooting

  • Output explodes / NaN: dt is too large, or you need a stiff solver. Reduce dt or switch to Trapezoidal / ESDIRK23. The status bar now names the block where divergence was first detected and the time it happened.
  • "Results may be inaccurate" warning: the solver ran out of sub-steps or its implicit solve did not converge. The numbers are still shown but are not trustworthy — reduce dt, or switch to a stiff solver (ESDIRK23) if the model has fast dynamics.
  • Oscillating output: Euler on a stiff system. Switch to Implicit Euler or Trapezoidal.
  • Simulation too slow: dt is too small or solver is inefficient. Try RK4 (fewer steps needed) or RK45 (auto-adapts).
  • Step response looks stepped: dt is too large to capture the dynamics. Reduce dt.
  • Plotter looks wrong at high zoom: Increase the Stop time or reduce dt for more data points.

Simulator & Solver Limitations

SysDynLab is designed for education and rapid prototyping. The following constraints apply:

Hard Limits (compile-time constants in the WASM engine)

ResourceLimitNotes
Blocks512Total flattened blocks after subsystem expansion. A subsystem with 50 blocks uses 50 of the 512 slots, not 1.
Wires2 048Total connections across the whole model.
Inputs per block8Sum, Mux, Product, Expression and similar blocks accept up to 8 inputs (a Plotter up to 10).
Continuous state slots6 144 (512 × 12)Integrator, filter, and TF state variables combined.
Implicit solver state size128Stiff solvers (Implicit Euler / Trapezoidal / ESDIRK23) build a dense Jacobian, which is capped at 128 continuous state variables. Above that the engine automatically runs RK45 instead and says so in the status bar.
SM states per block64Includes all hierarchy levels and parallel region states within one State Machine block.
SM transitions per block128Total transitions (all levels) within one State Machine block.
SM local variables16Per State Machine block.
Scope ring buffer2 M doubles (~16 MB)Shared across all Plotter channels. Wraps (oldest data overwritten) when full on long runs.

Numerical Precision

ItemLimitation
Floating-point precisionAll signals are 64-bit doubles (IEEE 754). Underflow near ±5×10−324 and overflow near ±1.8×10308 produce ±Inf or NaN — the simulation does not automatically detect or recover from this.
Fixed-step accuracyEuler and Heun accumulate global error proportional to dt. Halving dt roughly halves (Euler) or quarters (Heun) the error. For high-accuracy results use RK4 or variable-step RK45.
Variable-step resolutionRK45 and ESDIRK23 adapt step size internally but still record output at the user-specified dt intervals. Very coarse dt hides the fine-grained solution — reduce dt to improve plot resolution even with adaptive solvers.
Stiff systems with non-stiff solversEuler/Heun/RK4 require dt < 2 / |λmax| for stability. A fast pole (e.g. τ = 0.001 s) forces dt < 0.002 — which is often impractical. Switch to a stiff solver.

Model Constraints

ConstraintDetail
Algebraic loopsA feedback path with no state-holding block (Integrator, Unit Delay, Transfer Function, etc.) is an algebraic loop. The simulator warns and uses best-effort ordering, but results may be incorrect. Insert an Integrator or Unit Delay to break the loop.
Transfer function orderThe denominator polynomial must have degree ≥ numerator degree (proper or strictly proper). An improper TF (e.g. s/(s+1) is fine; s²/(s+1) is not) will cause incorrect or unstable behaviour.
Discrete sample time mixingAll discrete blocks share the simulation step dt as their base sample time. Blocks with different Ts values are evaluated at each step but only update when their counter triggers — this is not true multirate scheduling and can introduce up to one dt of sample-time error.
Vector signalsUp to 32 elements per wire. Math, nonlinear, continuous and discrete blocks work element by element; see the Vectors & Matrices tab for details.
Delay blockThe Transport Delay uses a ring buffer sized to ceil(delay / dt) samples. Very large delay-to-dt ratios consume memory proportionally. A delay of 10 s at dt = 0.0001 s allocates 100 000 doubles per delay block.
State machine expressionsArithmetic, comparisons, logic, bitwise operators, built-in math functions, and array locals / vector ports (buf[i]). No strings, no user-defined functions, and no ?: operator.
No zero-crossing detectionState machine transitions are evaluated at each time step, not at the exact moment a condition becomes true. A fast event shorter than dt may be missed. Reduce dt or use a discrete block with a matching Ts.

Browser & WASM Constraints

ConstraintDetail
WASM memory ceilingInitial heap is 64 MB; the runtime can grow up to 512 MB as needed. Very large scope buffers or many delay blocks may push into this range on long simulations.
Plotter data retentionScope data is held in browser memory. A 100 s simulation at dt = 0.0001 s stores 1 million data points per channel — this can slow plot rendering.
Single-threaded executionThe simulation runs on the main browser thread. Very long simulations will block the UI momentarily per frame.
No persistent storageModels are not saved automatically. Use Ctrl+S to download an XML file. Closing or refreshing the tab loses all unsaved work.
File accessCSV import, model load/save, and code download all use the browser file picker — there is no direct filesystem access.

What SysDynLab Does Not Support

  • Partial differential equations (PDEs) — only lumped (ODE) systems.
  • Distributed-parameter delay — only fixed transport delay with a ring buffer.
  • Multi-body physics or 3-D simulation.
  • Real-time hardware I/O — simulation is as-fast-as-possible, not wall-clock locked.
  • Symbolic math — all parameters must be numeric constants evaluated before simulation starts.
  • Automatic differentiation for sensitivity or gradient computation.
  • Optimization or parameter sweeps — run multiple simulations manually and compare Plotter traces.
Tip: For most control and signal-processing models these limits are never hit. If you encounter NaN outputs, check for algebraic loops first, then reduce dt. If the simulation is unexpectedly slow, switch from Euler to RK4 — it needs far fewer steps for the same accuracy.

Keyboard Shortcuts

ActionShortcut
UndoCtrl+Z
RedoCtrl+Y
Save modelCtrl+S
Open modelCtrl+O
Copy selectionCtrl+C
Cut selectionCtrl+X
PasteCtrl+V
Duplicate selectionCtrl+D
Select allCtrl+A
Delete selectionDelete  or  Backspace
Cancel wire / exit subsystemEscape
Apply block popupEnter (while popup is open)
Nudge selected blocksArrow keys (10 px)  /  Shift+arrow (1 px)
Remove last corner while drawing a wireBackspace
Open HelpF1 (Esc closes it)

Canvas & plots

ActionHow
Pan the canvasMiddle-button drag, or Alt+drag on empty canvas
Zoom in / outScroll wheel on canvas
Enter subsystemDouble-click a Subsystem block
Exit subsystemClick parent name in breadcrumb, or Escape
Open scope viewerDouble-click Plotter block after running
Zoom scope X or YClick Zoom X / Zoom Y → click two points on the plot
Pan scope plotDrag on the scope canvas
Zoom scope with wheelScroll on scope canvas
Reset scope viewClick Fit in scope toolbar

Phones and tablets

GestureDoes
Tap ☰ (top left)Show or hide the block library
Tap a library blockPlace it in the middle of the canvas
Press and hold a library block, then dragDrop it where you want it (a quick swipe scrolls the list instead)
Drag a block, port or wireMove it / draw a wire / move a wire segment, as with a mouse
Drag on empty canvas, or two-finger dragPan
PinchZoom
TapSelect; tap a port, then another, to connect them
Double-tap a blockOpen its settings (a Subsystem: go inside)
Long-press a block or wireIts right-click menu

Selecting, moving, wiring and the right-click menu are described in Blocks & Wiring.

On macOS the keyboard shortcuts use Ctrl (not Cmd); wire branching accepts either.

About SysDynLab

SysDynLab is a browser-based block diagram simulator for building and running continuous and discrete-time dynamic systems. It runs entirely in the browser using WebAssembly — no installation, no server-side computation.

Built with vanilla JavaScript, WebAssembly (Emscripten / C), and no external frameworks.

Disclaimer

No Warranty. Use at Your Own Risk.

SysDynLab is provided "as is", without warranty of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, or numerical accuracy.

The simulation results produced by SysDynLab are approximations based on the numerical methods and model parameters you configure. Results may differ from real-world behaviour due to solver discretization error, model simplifications, floating-point rounding, or incorrect parameter values.

The user assumes full responsibility for verifying the correctness of any simulation result before using it for design, engineering, safety analysis, or any other purpose. SysDynLab and its authors accept no liability for decisions made based on simulation output.

Do not rely on SysDynLab results in safety-critical applications without independent verification by qualified engineers using certified tools.

License

A note on the future of SysDynLab:
If there is enough community interest and people are willing to contribute, I am open to releasing SysDynLab as a fully open-source project on GitHub — so that everyone can improve it together. If you would like to see that happen, send a message via the Feedback button. Enough voices will make it happen.

SysDynLab is currently distributed under a Source-Available License. The source code is published for transparency and personal study. It is not open-source under OSI definitions.

SysDynLab Source-Available License
Copyright (c) 2025 SysDynLab. All rights reserved.

Permission is granted, free of charge, to any individual to:
  - Use this software for personal, academic, or educational purposes.
  - Read and study the source code for learning purposes.
  - Build and run the software locally for non-commercial use.
  - Create and share simulation models (.xml files) made with this software.

The following are expressly PROHIBITED without prior written permission
from the copyright holder:

  1. Hosting or deploying this software (or any modified version of it)
     as a public or private web service, website, or application.
  2. Redistributing, sublicensing, or publishing the source code or any
     substantial portion of it, whether modified or unmodified.
  3. Using this software or its source code as the basis for a competing
     product or service, commercial or otherwise.
  4. Removing or altering this license notice or any copyright attribution.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL
THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY
ARISING FROM THE USE OF OR INABILITY TO USE THIS SOFTWARE.

To request a commercial license or special permission, contact:
sysdynlab.simulator@gmail.com

Third-Party Components

ComponentPurposeLicense
EmscriptenCompiles the C simulation engine to WebAssemblyMIT / University of Illinois
WebAssemblyBrowser runtime for the simulation engineW3C open standard
Send Feedback ✕

Your feedback directly improves the simulator. Thank you!

▶ Code Generation Subsystem
Language
Function / Class Name
Integration
Data Type
C Standard
Optimization Level
Linkage
Plain Python 3, standard library only.