Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Plugin: Dynamical Systems Visualization

Authors
Affiliations
Virginia Tech, USA
Technical University of Denmark
Innatera

We built a Jupyter Book plugin that embeds dynamical systems visualizations as pure Python code via PyScript. That is, we can visualize differential systems written in Python in the browser. Which is pretty cool.

1Leaky Integrator Example

A simple leaky integrator system. The state v decays exponentially towards zero with time constant τ, driven by input x.

The system is defined by:

2How It Works

The simulator:

  1. Implements step(x, state, p) that computes one timestep

  2. x is the input (controlled by the Input slider)

  3. state and p are plain Python dicts for simplicity

  4. Returns (x_new, state_new) with updated state

Try adjusting the sliders:

3Usage

To create your own dynamical system, use the dynsim directive with Python code:

```{dynsim}
:params: [{"id": "param1", "label": "Label", "min": 0, "max": 1, "step": 0.1, "value": 0.5}]
:plotType: timeseries
:plotConfig: {"title": "My System"}
:initialState: {"your_var": 0}
:initialX: 0

import numpy as np

def step(x, state, p):
    # x: input from slider
    # state: your state variables (dict)
    # p: parameters including 'dt' (dict)

    # Your dynamics here (can use numpy functions)
    new_var = state['your_var'] + p['dt'] * (x - state['your_var'])
    x_new = new_var
    return (x_new, {'your_var': new_var})
```

4Why return a tuple?

The step function returns (x_new, state_new) — a tuple of two things:

  1. x_new — the output value that gets plotted.

  2. state_new — a dict with the updated state, fed back into the next call as state.

This separation exists because the plotted output and the internal state are not always the same thing. A neuron model, for example, might track a membrane voltage v and a recovery variable u internally, but only plot the voltage. Returning a tuple lets you choose what to display independently from what to remember. Note that the naming in the Tuple doesn’t matter, so you can call your variables whatever you want, for instance (y, s).

5Example: from equation to code

Say you want to simulate exponential decay:

v[t+1]=v[t]⋅(1−dtτ)v[t+1] = v[t] \cdot (1 - \frac{dt}{\tau})

To turn this into a step function:

  1. Read the current state from the state dict (state['v']).

  2. Read any parameters from p (p['tau'], p['dt']).

  3. Compute the next value using the equation.

  4. Return (output, new_state).

def step(x, state, p):
    v_new = state['v'] * (1 - p['dt'] / p['tau'])
    return (v_new, {'v': v_new})

Here x (the input slider) is unused — the system just decays on its own. If you wanted the input to drive the system, you could add it:

def step(x, state, p):
    v_new = state['v'] * (1 - p['dt'] / p['tau']) + x * p['dt']
    return (v_new, {'v': v_new})

6Directive Options

7How to run it locally

We had to hack the Jupyter book code, so to get this running you need to inject our hack and then re-start the server. Here is a sequence of commands you can run to make it work:

  1. rm -rf _build/site - clears the build cache

  2. jupyter book build - pulls the latest Jupyter book templates, including the code we’ll overwrite

  3. cp _static/js/server.js _build/templates/site/myst/book-theme/server.js - copies our own hacky script into the Jupyter book system

  4. jupyter book start - builds the book and starts a webserver you can access with your browser