Signal Studio is in public beta. Usable, but still moving. Join the beta

SDK reference

Everything a card can declare and everything it receives at run time. A card is a folder with a manifest and an implementation; there is no plugin API to learn beyond what is on this page.

The manifest

card.json declares what the card is, what it accepts, and every parameter that affects its output. It is also what draws the card in the interface: there is no separate UI to write.

FieldTypeMeaning
idstringUnique identifier, reverse-domain style
namestringLabel shown on the canvas
categorystringPalette section, dotted path
inputsarrayPorts the card accepts
outputsarrayPorts the card produces
paramsobjectEvery adjustable value
runobjectExecution phase and entry point

Port types

TypeCarries
RawContinuous recording, channels by samples
EpochsSegmented trials, trials by channels by samples
TableTabular results, one row per unit of analysis
MatrixSquare matrices, connectivity or distance
FigureA rendered plot
Raw|EpochsAccepts either; the card must handle both

Parameter types

TypeExtra keysRenders as
floatdefault, min, max, step, unitNumeric field with unit
intsameInteger field
booldefaultToggle
enumvalues, defaultDropdown
stringdefault, hintText field
channelsmultipleChannel picker from the input
The rule the built-in cards follow: anything that changes the result has to appear in params. A value hard-coded in the implementation is treated as a bug, because it is invisible to the reader.

The run context

The implementation exposes a single function, run(ctx). The context carries the resolved inputs and parameters, and the helpers.

MemberDescription
ctx.inputs["id"]The object arriving on that port
ctx.params["id"]Resolved parameter value, after defaults and auto-resolution
ctx.table(**cols)Build a Table output from named columns
ctx.matrix(array, labels)Build a Matrix output
ctx.log(msg)Write to the run log, visible in the interface
ctx.progress(fraction)Report progress from 0 to 1
ctx.cache_key()Stable key for the current inputs and parameters

A complete card

spectral_entropy/card.json
manifest
{
  "id": "user.spectral_entropy",
  "name": "Spectral Entropy",
  "category": "analysis.features",
  "inputs":  [{ "id": "in",  "type": "Raw|Epochs" }],
  "outputs": [{ "id": "out", "type": "Table" }],
  "params": {
    "fmin":      { "type": "float", "default": 1.0,  "unit": "Hz" },
    "fmax":      { "type": "float", "default": 40.0, "unit": "Hz" },
    "n_per_seg": { "type": "int",   "default": 512 },
    "window":    { "type": "enum",  "values": ["hann", "hamming"] }
  },
  "run": { "phase": "analysis", "entry": "run.py" }
}
spectral_entropy/run.py
python
import numpy as np
from scipy.signal import welch


def run(ctx):
    x = ctx.inputs["in"]
    f, pxx = welch(
        x.get_data(),
        fs=x.info["sfreq"],
        nperseg=ctx.params["n_per_seg"],
        window=ctx.params["window"],
    )
    m = (f >= ctx.params["fmin"]) & (f <= ctx.params["fmax"])
    p = pxx[..., m] / pxx[..., m].sum(axis=-1, keepdims=True)
    h = -(p * np.log(p + 1e-12)).sum(axis=-1)
    return { "out": ctx.table(entropy=h, unit="bits") }

Execution phases

The phase in run tells the engine where the card belongs in the pipeline, which determines caching and ordering.

PhaseRuns on
ioLoading and writing files
preprocessing.spatialRe-referencing, channel operations
preprocessing.temporalFiltering, resampling
epochingSegmentation and trial rejection
analysisFeatures, statistics, decomposition
visualisationFigures

Installing a card you wrote

Drop the folder into your user cards directory and it appears in the palette. No build step.

Windows%APPDATA%\Signal Studio\cards\user\
macOS~/Library/Application Support/Signal Studio/cards/user/
Linux~/.config/Signal Studio/cards/user/

To share it, zip the folder and publish it to the marketplace.

Errors

Raise a normal Python exception. The message reaches the interface and the run log, so make it say what a user can act on: which parameter, which channel, what was expected.