ReferencesVisualization Contract

Visualization Contract

Modules implement visualize() to return one visual spec or a list of them. Labs Serve UI and the platform render these specs after transport normalization.

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"]])

Visuals built by a downstream module

A module that plots another module’s outputs only sees them after they are committed at a boundary. Give that module ExecutionPolicy.ONCE_AFTER_RUN. BioWorld runs it once after the final window, with the final committed inputs.

import biosimulant as biosim
 
 
class PopulationTable(biosim.BioModule):
    execution_policy = biosim.ExecutionPolicy.ONCE_AFTER_RUN
 
    def __init__(self):
        self.final_count = None
 
    def inputs(self):
        return {"population_state": biosim.SignalSpec.record(schema={"count": "int64"})}
 
    def execute(self, inputs, *, context):
        state = inputs.get("population_state")
        if state is not None:
            self.final_count = state.value["count"]
        return {}
 
    def visualize(self):
        return {
            "render": "table",
            "data": {
                "columns": ["Metric", "Value"],
                "rows": [["Final count", str(self.final_count)]],
            },
        }

Do not use world.settle() or runtime.settle_steps for this. Settling skips modules that implement execute() and only calls modules that implement advance_window().

Spec fields

FieldTypeRequiredDescription
renderstrYesVisual type. Known types are "timeseries", "bar", "graph", "table", "image", "text", and "structure3d", plus "custom:<name>" for your own renderer. Validation only requires a non-empty string.
datadictYesRender-type-specific, JSON-serializable content
descriptionstrNoOptional text description

For structure3d file sources, see Artifact Outputs.

Timeseries

def visualize(self):
    return {
        "render": "timeseries",
        "data": {
            "title": "Membrane Potential",
            "xlabel": "Time (ms)",
            "ylabel": "Voltage (mV)",
            "series": [
                {"name": "Neuron 1", "points": self.v1_history},
                {"name": "Neuron 2", "points": self.v2_history}
            ]
        }
    }
FieldTypeDescription
titlestrChart title
xlabelstrX-axis label
ylabelstrY-axis label
serieslist[{name, points}]Named data series

Each points entry is a list of [x, y] pairs: [[0.0, -65.0], [0.1, -60.2], ...].

Bar Chart

def visualize(self):
    return {
        "render": "bar",
        "data": {
            "title": "Spike Counts",
            "items": [
                {"label": "Excitatory", "value": self.exc_count},
                {"label": "Inhibitory", "value": self.inh_count}
            ]
        }
    }
FieldTypeDescription
titlestrChart title
itemslist[{label, value}]Labeled values

Table

def visualize(self):
    return {
        "render": "table",
        "data": {
            "columns": ["Metric", "Value"],
            "rows": [
                ["Mean Rate", f"{self.mean_rate:.2f} Hz"],
                ["Total Spikes", str(self.total_spikes)]
            ]
        }
    }
FieldTypeDescription
columnslist[str]Column headers
rowslist[list[str]]Row data (strings)

Image (Raster Plot, Heatmap, etc.)

For complex visualizations generated with matplotlib or similar:

def visualize(self):
    return {
        "render": "image",
        "data": {
            "src": self.raster_image_base64,  # data:image/png;base64,... or raw base64
            "alt": "Spike Raster",
            "width": 800,
            "height": 400
        }
    }
FieldTypeDescription
srcstrBase64-encoded image (PNG/JPEG)
altstrAlt text
widthintDisplay width in pixels
heightintDisplay height in pixels

Tips

  • Keep timeseries histories bounded or downsampled for long runs.
  • Put units in axis labels.
  • Keep lab visualisation modules inside the lab when you distribute portable .bsilab archives. A lab that imports a shared visualisation package is no longer self-contained.

See Also