How ToLibraryCustom Visualizations

How to Add Custom Visualizations

Modules can return visualization specs from visualize(). SimUI and platform consumers render these automatically after transport normalization.

The visualization contract

Return either one visual spec or a list of them:

def visualize(self):
    return {
        "render": "timeseries",
        "data": {...},
    }

Timeseries

def visualize(self):
    return {
        "render": "timeseries",
        "data": {
            "title": "Population Over Time",
            "xlabel": "Time",
            "ylabel": "Count",
            "series": [
                {"name": "Prey", "points": self.prey_history},
                {"name": "Predator", "points": self.predator_history},
            ],
        },
    }

Bar chart

def visualize(self):
    return {
        "render": "bar",
        "data": {
            "title": "Spike Counts by Population",
            "items": [
                {"label": "Excitatory", "value": self.exc_count},
                {"label": "Inhibitory", "value": self.inh_count},
            ],
        },
    }

Table

def visualize(self):
    return {
        "render": "table",
        "data": {
            "columns": ["Metric", "Value"],
            "rows": [
                ["Mean Firing Rate", f"{self.mean_rate:.2f} Hz"],
                ["Total Spikes", str(self.total_spikes)],
            ],
        },
    }

Image

def visualize(self):
    return {
        "render": "image",
        "data": {
            "src": "data:image/png;base64,...",
            "alt": "Spike raster",
            "width": 800,
            "height": 400,
        },
    }

Collect visuals from a world

world.run(duration=10.0)
visuals = world.collect_visuals()
 
for entry in visuals:
    print(entry["module"], [v["render"] for v in entry["visuals"]])

If the visualisation is a separate downstream module, its inputs are not visible until after the producer’s outputs have been committed. Run a final settle turn before collecting visuals:

world.run(duration=10.0)
world.settle(1)
visuals = world.collect_visuals()

In lab.yaml, use runtime.settle_steps: 1 for the same direct producer-to-visualisation pattern.

Tips

  • Keep timeseries histories bounded or downsampled for long runs.
  • Use units in labels where possible.
  • Return JSON-serializable data or artifact-backed image outputs.
  • Keep lab visualisation modules lab-local when distributing portable .bsilab archives. Shared repository copies can be checked for drift, but labs should not depend on a shared runtime visualisation package unless portability is no longer a requirement.

Next steps