Skip to main content

SCD Studio

Python 3.10+ PyPI License: BSD 3-Clause

A graphical application for researchers and engineers to decompose high-density surface or intramuscular EMG recordings into individual motor unit spike trains, edit them manually, and visualise population-level discharge behaviour.

Supports OT Bioelettronica .otb+/.otb4, Intan .rhs, MATLAB, HDF5, NumPy and CSV recordings on Windows, Linux and macOS.

SCD Studio demo

Interface at a glance

Configuration Decomposition
Configuration tab with the bundled example loaded Decomposition tab showing live source feedback
Load recordings, define grids and auxiliary channels, and review the channel map. Configure and monitor decomposition with live source and quality feedback.
Edition Visualisation
Edition tab previewing an automatic motor-unit split Visualisation tab showing population-level motor-unit plots
Review quality, edit spike trains, split merged units, inspect individual MUAPs and manage duplicates. Explore raster, discharge-rate, recruitment, force and quality summaries.

Built on the Swarm Contrastive Decomposition (SCD) algorithm. The desktop interface uses the official Qt for Python (PySide6) bindings.


Table of Contents

  1. Installation
  2. Two-minute example
  3. Choosing a tool
  4. Launching the app
  5. Complete workflow
  6. Saving and loading
  7. Keyboard shortcuts
  8. File formats
  9. Importing an unfamiliar format
  10. Force channel setup
  11. Citation
  12. Diagnostics and logs
  13. Contributing
  14. License

Installation

pip install scd-studio

Windows users who do not want to manage Python can instead download the windows-x86_64.zip archive from the latest GitHub Release, extract it and run SCD-Studio.exe. This standalone build is CPU-only; use a source installation with the cuda extra for NVIDIA GPU acceleration.

If SCD Edition 0.1.x is already installed, replace the old distribution once:

pip uninstall scd-edition
pip install scd-studio

The scd-edition command remains available as a compatibility alias, and existing decomposition files continue to open without conversion.

Latest development version

pip install git+https://github.com/AgneGris/scd-studio.git

All dependencies install automatically.

On Windows, this route may install a CPU-only PyTorch build. Use the CUDA-enabled uv route below to ensure NVIDIA GPU support.

uv manages the virtual environment and dependencies automatically.

Clone the repository, then choose the command for your platform:

git clone https://github.com/AgneGris/scd-studio
cd scd-studio

On Windows, install and require a uv-managed Python to avoid DLL conflicts with Conda or Anaconda:

uv python install 3.13

Windows — CUDA-enabled (recommended if you have an NVIDIA GPU):

uv sync --python 3.13 --managed-python --extra cuda
.venv\Scripts\Activate.ps1

Linux — CUDA-enabled:

sudo apt-get install libegl1 libxkbcommon-x11-0 libxcb-cursor0
uv sync --extra cuda
source .venv/bin/activate

macOS or CPU-only Linux:

uv sync --extra cpu
source .venv/bin/activate

On CPU-only Linux, install the same Qt runtime packages shown in the CUDA-enabled Linux example before launching the GUI. Package names may differ on distributions that do not use apt.

CPU-only Windows:

uv sync --python 3.13 --managed-python --extra cpu
.venv\Scripts\Activate.ps1

The cpu and cuda extras are mutually exclusive, and uv does not remember which one was used by an earlier sync. When running project commands through uv run, repeat the selected extra. For CUDA, use uv run --extra cuda scd-studio. Alternatively, use uv run --no-sync after a successful sync. A bare uv run may replace the installed PyTorch variant.

From source with pip

git clone https://github.com/AgneGris/scd-studio
cd scd-studio
pip install -e .

Two-minute example

SCD Studio includes a 64-channel, 10-second surface EMG example. Open the raw recording with its loader, sampling rate and electrode layout already filled in:

scd-studio --example

Then:

  1. Click Apply Configuration.
  2. Click Start Decomposition and confirm the automatically selected channels.
  3. Select Full file, or choose a short time interval for a faster first run.
  4. Review the resulting units in Edition and Visualisation.

The example is for learning the workflow, not for benchmarking decomposition accuracy. See the illustrated quick-start or the executable tutorial notebook for more detail.

The raw recording and matching ten-unit demo decomposition are tracked together as examples/scd-demo/emg.mat and examples/scd-demo/emg_decomp_output.pkl. See the example-data notes for the file contents and instructions for opening either the raw recording or completed decomposition.

To explore Edition without running a new decomposition, open the completed .pkl from the Edition tab. This compact example stores the ten source signals and spike trains but does not embed another copy of the raw EMG, so MUAP display and filter recalculation are unavailable when it is opened by itself. The same example data are also used by the Swarm-Contrastive Decomposition project.

Choosing a tool

SCD Studio is one of several strong tools for motor-unit decomposition. The right choice depends on the algorithm, acquisition system and analysis workflow; this table describes scope rather than ranking performance.

Tool Decomposition and scope Environment and access Particularly useful when
SCD Studio Swarm Contrastive Decomposition; surface and intramuscular EMG; integrated review, spike editing and population plots Open-source BSD Python/Qt app; PyPI and a standalone Windows release You want the SCD method and a reproducible path from raw recording to edited units
DEMUSE CKC decomposition, accuracy assessment and manual editing for multichannel surface EMG Commercially available MATLAB program You want the established CKC/DEMUSE workflow
OTBioLab+ Manufacturer acquisition and processing suite with motor-unit decomposition Vendor-distributed desktop software You acquire with OT Bioelettronica hardware and want its native workflow
openhdemg Convolutive BSS plus broad import, analysis, tracking and editing functionality for HD-sEMG Open-source BSD Python framework with graphical software You need a broad motor-unit analysis library or interoperability with several decomposition formats
MUedit fastICA decomposition and manual pulse-train editing Open-source MATLAB app; You want the published MUedit protocol and its fastICA workflow

Algorithm outputs should be reviewed using physiological criteria and validation appropriate to the study; this table is not evidence that the tools are interchangeable.

Usage 🚀

Launching the app

scd-studio

Or, if the entry point is not on your PATH:

python -m scd_app

The application opens with four tabs along the top. Work left to right: configure → decompose → edit → visualise.

You can also skip the first two tabs entirely: decompose with the swarm-contrastive-decomposition package in a script and open its output here — see Decomposing with the SCD package, editing here.


Decomposing with the SCD package, editing here

The Decomposition tab runs the same algorithm as the swarm-contrastive-decomposition Python package, so you can decompose in a script or on a cluster and use this app only for editing. Save the results with the package's save_results, then load the .pkl with Load Decomposition in the Edition tab:

import scd

dictionary, timestamps = scd.train("recording.mat", config_name="surface")
scd.save_results("recording_surface.pkl", dictionary)

Since version 0.2.3 the package stores the loaded signal in the results by default (save_data), together with the trimming window and rejected channels it decomposed with. With that in the file, the Edition tab has everything an in-app decomposition would have: full-length sources, MUAP display, filter recalculation and spike re-detection. Note that the stored signal makes the file larger by channels × samples × 4 bytes; the package README explains how to turn it off.

What differs from an in-app decomposition:

  • The file holds one grid, named after the muscle-<name> part of the filename, or the filename itself.
  • No electrode geometry is recorded, so MUAPs are shown in the default grid layout, and there are no force or auxiliary channels.
  • Files written with save_data=False, or by package versions before 0.2.3, open without the signal: spike trains and sources can still be edited and saved, but MUAPs, filter recalculation and re-detection are unavailable.

The silhouette (SIL) is not the same number in the two tools

The package accepts sources using a silhouette it computes during decomposition; this app computes its own SIL for the Quality chart and the reliability flag. They are different quantities and should not be compared or thresholded interchangeably. This also applies to decompositions run in the Decomposition tab, whose SIL Threshold is the package's acceptance silhouette.

swarm-contrastive-decomposition (dictionary["silhouettes"]) SCD Studio (MU properties, Quality chart)
When During decomposition, at the iteration that accepted the unit; frozen in the output After loading and after every edit, from the unit's current spikes
Source The source of that iteration: computed from the peeled EMG (earlier units removed), restricted to the decomposition window, edge-masked, clamped, squared The full-length source recomputed on load (spike-triggered-average filter on the whole recording, z-scored on the decomposition window), squared; not clamped or edge-masked
Points Every peak of the squared source at least reset_peak_separation_ms apart — spikes and baseline peaks Spikes: the unit's current timestamps. Baseline: every peak lower than the smallest spike
Classes Two-class k-medians on peak heights; spikes are the upper cluster No clustering — the spike set is whatever the unit currently has
Score For each peak, a = distance to its own centroid, b = distance to the other centroid; SIL = mean over all peaks of (b − a) / max(a, b) Only spike amplitudes enter: d_s = Σ (s − mean_spikes)², d_b = Σ (s − mean_baseline)²; SIL = (d_b − d_s) / max(d_b, d_s) (motor_unit_toolbox.get_silhouette_measure, the formulation of Negro et al., 2016). Squared distances, summed; the baseline enters only through its mean
Threshold acceptance_silhouette (0.85 default, 0.8 in the surface preset) decides whether a source is accepted SIL ≥ 0.9 marks a unit as reliable

In practice the two values are often close for well-separated units, but they diverge when the baseline peaks are numerous or spread out (the package averages over them, the toolbox ignores their spread), when a unit has been edited (only this app's value follows the edits), and whenever the two sources differ (window vs full recording, peeled vs recomputed filter). The package's values are kept in the file under scd_metadata["silhouettes"] for reference but are not displayed.


Complete workflow

Tab 1 — Configuration

This tab is where you tell the application about your recording before doing anything else.

1. Select your input file

Click Choose recording… and select your EMG recording. Supported formats:

Extension Format
.otb+ OTBiolab+ (Quattrocento amplifier)
.otb4 OTBiolab 4 (Novecento+ amplifier)
.rhs Intan RHS2000 (Stim/Recording Controller)
.mat MATLAB
.h5 HDF5
.npy NumPy array
.csv Comma-separated values

Use Choose batch… to select multiple recordings that share one format and channel layout. They will be decomposed sequentially with the same configuration.

If a MATLAB, HDF5, NumPy, CSV, or text file does not match the selected format, the array inspector opens automatically. You can also reopen it with the advanced Inspect arrays… action beside Data Format. It lists numeric arrays, lets you choose the EMG matrix and orientation, and previews the resulting sample/channel shape. See Importing recordings without a built-in loader.

2. Set the sampling rate

Enter the sampling rate of your recording in Hz (e.g. 2048 for a Quattrocento at 2 kHz). For formats that carry it in the file (.otb4, .rhs) it is filled in automatically.

Decimate by (next to the sampling rate) reduces the rate the decomposition works at by an integer factor, for any file format. The loader low-pass filters (zero-phase FIR) before subsampling, so nothing aliases, and the delivered rate is shown beside the box. Use it for recordings sampled far above the amplifier bandwidth — e.g. a 20 kHz Intan file band-limited at 500 Hz decimated by 5 to 4 kHz — so that extension factors and MUAP windows stay meaningful in milliseconds and runtime drops accordingly. Loader presets can seed the box (decimate: in the loader YAML); the value is saved with the configuration. The factor must divide the sampling rate exactly.

3. Add electrode grids

Click + Add Grid for each electrode array in your recording. For each grid, specify:

  • Name — a label you choose (e.g. Biceps, FDI)
  • Muscle — optional, for your own reference
  • Type — Surface or Intramuscular
  • Electrode model — select from the dropdown (e.g. GR10MM0808 for a 64-channel 8×8 surface grid)
  • Channel start / end — which channels in the file correspond to this grid (0-indexed, end is exclusive). The colour bar at the top shows how channels are allocated across all grids.

4. Add force / auxiliary channels (optional)

If your recording includes force or other analogue channels, click + Add Aux Channel for each one. Specify:

  • Name — e.g. Middle Ext
  • Unit label — e.g. Middle Ext (used to auto-select the correct channel when visualising named tasks)
  • Source — Signal if force is stored as regular channels in the EMG array; Auxiliary stream for OTB+ .sip or Novecento+ external/AUX tracks
  • Channel start / end — channel indices within the file (or sip stream)
  • MVC (mV) — the maximum voluntary contraction value in millivolts. This is used to normalise force to %MVC in the visualisation. See Force channel setup for how to find this value.

5. Save / load your configuration

Click Save Config to export the full configuration (file path, grids, aux channels, parameters) to a JSON file. Click Load Config to restore a previously saved configuration. The input file is not overwritten when loading a config — the file you selected in step 1 is preserved.

6. Apply

Click Apply Configuration. This validates all settings and prepares the decomposition tab. If anything is wrong (e.g. channel indices out of range) an error will appear here.


Tab 2 — Decomposition

Global parameters

These apply to all grids:

Parameter What it does
SIL Threshold Minimum silhouette score for a source to be accepted as a motor unit. Higher = stricter (fewer but more reliable MUs). Default 0.9. This is the decomposition algorithm's own silhouette, computed differently from the SIL shown in the Quality chart — see The silhouette is not the same number in the two tools.
Iterations Maximum number of optimisation steps per source. More iterations → longer runtime but potentially more MUs found.
MUAP Window (ms) Duration of the spike-triggered-average window used for peel-off and for MUAP displays in the Edition tab.
Fitness Optimisation criterion: SIL (silhouette) or CoV (coefficient of variation of ISI).
Peel-off Whether to subtract each found motor unit from the signal before searching for the next. Recommended: on.
Swarm mode Enables the particle swarm optimiser. Recommended: on.
Adapt clamp Adaptive clamping during whitening. Recommended: on.

Per-grid parameters

These can be set independently for each electrode grid by selecting the grid from the dropdown:

Parameter What it does
SIL Threshold Per-grid SIL acceptance criterion (overrides global for this grid).
Extension Factor Number of delayed copies of each channel used to extend the observation space. Larger values capture more motor unit information but increase computation time. Typical: 10–30 for surface, 20–40 for intramuscular.
High-pass (Hz) High-pass filter cutoff. Use ≥10 Hz for surface, ≥20 Hz for intramuscular.
Low-pass (Hz) Low-pass filter cutoff. 4400 Hz is typical for surface at 10 kHz; adjust to ~half the Nyquist of your sampling rate.
Notch filter Remove power-line interference: None, 50 Hz (Europe), or 60 Hz (Americas).
Notch harmonics Also remove harmonics (100, 150 Hz etc.) when notch is active.

Batch processing options

When multiple files are queued:

  • Shared rejection — perform channel rejection on the first file only, then apply the same rejection mask to all subsequent files. Useful when recording conditions are stable across files.
  • Per-file rejection — perform channel rejection independently for each file.

Starting decomposition

  1. Click Start Decomposition.
  2. A signal plot appears. Noisy or broken channels are shown as dashed lines. Click any channel to toggle rejection (rejected channels are excluded from decomposition).
    • Scroll to zoom the time axis
    • Shift+Scroll to pan
    • Right-drag to pan
    • R to reset the view
  3. Optionally set a time window (plateau) to decompose only a steady-state segment. Click on the plot to set start and end points, or enter times manually.
  4. Click Confirm to start the actual decomposition. Progress and found sources are shown in real time.
  5. Click Stop at any time to halt early. Results from completed grids are saved.

When decomposition finishes, the results are automatically saved to a .pkl file in the configured output folder, and the Edition tab opens.


Tab 3 — Edition

This is the main editing environment. It shows one motor unit at a time.

Navigating between units

  • Use the Port dropdown or the Ctrl+Up/Down arrow keys to switch between electrode grids.
  • Use the Unit dropdown or the Up/Down arrow keys to switch between motor units within a port.
  • Click Mark Reviewed (or press M) after checking a unit. Next Unreviewed (or N) advances through unfinished units across all ports, and the progress label shows the reviewed total. Review status is separate from the automatic reliability verdict and is saved with the session. Editing a reviewed unit's spikes or source signal resets it to unreviewed so the change is checked again.
  • The properties panel on the right updates automatically:
    • Spike count, mean discharge rate, CoV ISI, minimum ISI
    • SIL and MUAP template stability — complementary source-separation and waveform-consistency measures.
    • Reliability badge — a unit is automatically RELIABLE when every automatic criterion passes: SIL ≥ 0.9, CoV ISI ≤ 40 %, discharge rate 3–40 Hz and at least 10 spikes. Hover the badge to see which criteria a unit fails. Left-click the badge (or press T) to override the verdict by hand — an overridden badge is italic and marked (manual), and the unit is shown as ✓* / ✗* in the unit dropdown. Right-click (or press Shift+T) to go back to the automatic verdict. Manual verdicts are saved with the session.
    • MUAP amplitude, waveform length, peak and median frequency
    • Duplicate warning — after an explicit duplicate scan, shows the current unit's matched partners and whether it is suggested for keeping or deletion

MUAP template stability is the normalized correlation (0–1) between two spike-triggered average templates made from alternating discharges. It is calculated from the original multichannel EMG, so it is unavailable when raw EMG is not embedded in the file or when fewer than five valid discharges are available for either half. It is shown as a descriptive consistency measure, not used as an automatic acceptance threshold.

PNR is intentionally not reported for SCD units. Although the conventional 30 dB boundary is familiar in the field, it was validated for CKC sources and does not have the same interpretation for SCD's normalized source signals.

Source signal plot

The large plot on the left shows the squared source signal (the spatial filter output) with spike locations marked as orange circles. A shaded region shows the plateau used for decomposition; outside this region the signal is reconstructed by replaying the peel-off sequence over the full recording.

Right-click an orange spike marker to inspect its multichannel MUAP. The MUAP panel overlays that discharge as a thin solid orange line on a thicker blue leave-one-out reference made from the unit's other valid discharges. The header reports normalized waveform similarity (r), amplitude ratio and the small alignment lag used for the comparison. This is a review aid rather than an automatic keep/remove rule: overlapping activity and noise can reduce the score of a genuine discharge.

The selected orange waveform starts in Raw EMG mode. Switch to Earlier units removed to subtract only the units that came before the current unit in the original peel-off sequence; MU 0 therefore has nothing to subtract. This switch changes the orange waveform and its reported measurements, while the blue reference stays fixed. Use the Selected spike toggle to hide or show the orange overlay. While a spike is selected, Left/Right, [/], or the previous/next buttons move through the unit's spikes. Right-click the same spike again, press Escape, or select another unit or port to finish the comparison.

Scrolling / zooming:

  • Scroll — zoom in/out along the time axis
  • Shift+Scroll — scroll horizontally
  • Ctrl+Scroll — zoom in/out in both axes
  • Home — reset view to show the full signal

If force channels are configured, a force trace is overlaid on the source plot. The right y-axis shows % MVC, scaled automatically to the actual force range.

The ... menu on interactive plots provides View all and Export plot. Generic PyQtGraph transformations such as FFT, derivative, logarithmic axes and Y vs Y' are intentionally not exposed because they change the live plot state and can disrupt linked GUI views.

Editing modes

Mode How to activate What it does
View Default; press Esc to return Navigate, zoom and inspect spikes without editing.
Add spikes A or button Toggle on, then drag a box to add all enclosed peaks. Press A again to turn it off.
Delete spikes D or button Toggle on, then drag a box to remove all enclosed spikes. Press D again to turn it off.

Press Ctrl+Z to undo (up to 100 steps) and Ctrl+Y to redo.

Splitting a merged unit

Click Split Unit when one source appears to contain two motor units. The preview partitions every spike from its squared source-peak height: the taller population is orange A, and the lower population is cyan B. Click an individual marker or drag a rectangle to move spikes between groups. Each group must retain at least two spikes.

Click Confirm Split to create [split A] and [split B], or click the now-labelled Cancel Split button (or press Escape) to discard the preview without changing the unit. After confirmation each child displays only its own orange markers and can be edited like any other unit.

For the cleanest sequential separation, finish cleaning the high-amplitude A train first. Then select split B and click Recalculate Filter. The app peels A from the multichannel residual only at A's current curated timestamps before estimating B; it does not directly subtract one normalized source trace from another. Recalculate B again if A is edited later.

MUAP plot

Shows the spike-triggered average for every EMG channel. Toggle between stacked and grid layout (the grid layout reflects the physical electrode geometry). In the stacked view, scroll zooms the time axis, Shift+Scroll pans horizontally and Ctrl+Scroll zooms both axes. Click a channel to open a pop-out window with a larger view.

Quality actions

Button Shortcut Effect
Recalculate Filter F Re-estimates the spatial filter from the edited spike train, recomputes the source, and snaps the retained timestamps to nearby peaks. For split B, curated split A is peeled first. Requires ≥2 spikes.
Split Unit — Suggests high-amplitude A and low-amplitude B groups, opens an editable preview, and creates two independently editable units only after confirmation.
Auto-edit E Automatically removes obvious outlier spikes based on physiological firing rate limits.
Remove outliers O Removes spikes with very short or very long ISIs.
Flag unit X Manually marks the unit for deletion.
Mark Reviewed M Marks or unmarks the current unit as manually reviewed.
Next Unreviewed N Moves to the next unreviewed unit, including across ports.
Delete All Flagged Units — Permanently removes flagged units from every port and clears undo/redo history. A confirmation lists the count for each affected port.
Check Duplicates in Current Port — Runs rate-of-agreement on demand for every unit in the selected port, including flagged units, and suggests the lower-priority member of duplicate pairs for deletion. An existing flag cannot cause an unflagged partner to be suggested. Excessively large comparisons are skipped.
Check Duplicates Across Ports — Optionally performs the same on-demand comparison across grids/probes. Use this only when ports may record overlapping motor-unit populations. Excessively large comparisons are skipped.

Duplicate detection

Rate-of-agreement (RoA) is not calculated while a decomposition is loading or during ordinary MU-property updates. It runs only when you click one of the two duplicate-check buttons, using a 30% agreement threshold. This avoids making every load and spike edit pay the cost of a potentially large all-pairs comparison.

Within one port, the estimated workload is the source length multiplied by the square of the unit count. Across ports, it is the source length multiplied by the two ports' unit counts. A comparison is skipped if that estimate exceeds 1,000,000,000; the results dialog identifies every skipped or failed port or port pair instead of presenting an incomplete scan as a clean result.

A scan only suggests and flags the lower-priority members of duplicate pairs; review the marked units before using Delete All Flagged Units. Changing a source or any spike timestamps makes previous agreement scores stale, so the app clears the current port's within-port results and all cross-port results. The status bar then asks you to rerun the duplicate checks.

Saving

Ctrl+S saves the current state to a .pkl file. The first save opens a dialog and remembers the chosen destination; subsequent saves to the same file happen silently. Use File → Save As… (Ctrl+Shift+S) to choose a different destination. The saved file contains the edited spike trains, spatial filters, raw EMG, force data, and all metadata needed to reload and continue editing later.


Tab 4 — Visualisation

This tab shows population-level summaries for all motor units in the loaded decomposition. It updates automatically whenever you switch to it or edit spikes in Tab 3.

The left panel lists all motor units by port. Click a unit to toggle it on/off. Use All / None to show or hide all units at once. The Sort dropdown reorders units by recruitment threshold, index, or mean discharge rate.

If force channels are configured, they appear as overlays on the time-domain plots. Click the legend (top-right of each plot) to toggle individual channels. The right y-axis shows % MVC, automatically scaled to the actual force range of the recording.

Sub-tabs

Raster — each row is one motor unit; vertical marks show individual spike times. Force is overlaid at the bottom, scaled to its actual %MVC range. The full force recording is shown even if only part of the signal was decomposed.

IDR (Instantaneous Discharge Rate) — smoothed discharge rate (pps) over time for each unit. Useful for checking that firing rates are physiologically plausible and that units track the force task.

CST (Cumulative Spike Train) — sum of all discharge rate traces. Approximates the neural drive to the muscle.

Quality — SIL and MUAP template-stability bar charts for all units. The SIL chart shows the automatic reliability threshold (SIL ≥ 0.9); template stability is descriptive and therefore has no threshold line. This SIL is computed by the app from the unit's current spikes and is not the silhouette the decomposition algorithm used to accept the source — see The silhouette is not the same number in the two tools.

DR vs Force — scatter plot of each motor unit's recruitment force (%MVC at first spike) against its mean discharge rate during the plateau. A regression line is drawn when three or more units are present. This plot requires at least one active force channel.


Saving and loading

Decomposition results are stored as .pkl (Python pickle) files. Each file contains:

Security: Python pickle files can execute code while they are opened. Only load decomposition files that you created yourself or received from a source you trust. Selected files are opened immediately.

  • Raw EMG data (all channels, full recording)
  • Spike timestamps for every motor unit
  • Spatial filters (original and edited)
  • Force / auxiliary channel data (full recording)
  • Channel layout, rejection mask, electrode geometry
  • Peel-off sequence (for filter recalculation)
  • Quality metrics

Every new or edited decomposition is accompanied by a small <output-name>.audit.json reproducibility report. For example, result.pkl is accompanied by result.audit.json. The report records the software and compute environment, input filename, processing window, grid parameters, rejected channels, detected and retained motor-unit counts, and a summary of later edits. It does not contain signal samples, spike timestamps, note text, or absolute paths. Input and output filenames can still contain participant identifiers, so review an audit report before sharing it publicly.

To reload a decomposition: in Tab 3, click Load Decomposition and select the .pkl file. The app automatically recognises both SCD Studio files (including files created by SCD Edition 0.1.x) and raw *_scddict.pkl output from swarm-contrastive-decomposition. Raw SCD output is converted into a one-grid Edition session; the muscle label in the filename is used as the grid name, and any companion *_scdcommit.txt is retained as provenance.

SCD Studio files that contain the original EMG can replay peel-off and recalculate filters. Raw swarm-contrastive-decomposition output contains it too when saved with the package's default save_data=True (version 0.2.3 or later), so it gets the same treatment; output saved without the signal remains editable and can be saved from Edition, but filter/MUAP recalculation is unavailable. See Decomposing with the SCD package, editing here.

When a previously edited file contains the original EMG, the app reopens it on the full recording, preserving saved spike edits even outside the original decomposition window, and asks whether to recalculate every motor-unit filter from the saved edited spike trains. Choose No (the default) to retain the saved filters, or Yes to re-estimate them during peel-off replay. The same choice is now offered when the edition contains split units: split A is processed before split B so B is estimated from the residual after A has been peeled at its curated timestamps. Legacy split sessions are upgraded to this two-step A-to-B replay when loaded.

For an unedited decomposition that covered only part of the recording, a separate prompt asks whether to re-detect timestamps over the full signal or keep the timestamps from the original decomposition window.

Saved Edition files can be reloaded in any order and remain fully editable.


Keyboard shortcuts

Global shortcuts:

Key Action
Ctrl+1 Switch to Configuration tab
Ctrl+2 Switch to Decomposition tab
Ctrl+3 Switch to Edition tab
Ctrl+4 Switch to Visualisation tab

Edition shortcuts (after a decomposition is loaded):

Key Action
A Toggle Add spikes; drag a rectangle to add enclosed peaks
D Toggle Delete spikes; drag a rectangle to remove enclosed spikes
Esc Disarm selection or close the current spike-MUAP comparison
F Replay peel-off and recalculate the current unit's filter, source and timestamps
O Remove instantaneous-firing-rate outlier spikes from the current unit
E Run rule-based auto-editing on the current unit
X Toggle the deletion flag on the current unit
M Toggle the manually reviewed status of the current unit
N Move to the next unreviewed unit across all ports
T Toggle the reliability verdict of the current unit
Shift+T Reset reliability to the automatic verdict
Ctrl+Z Undo (Cmd+Z on macOS)
Ctrl+Y Redo (Cmd+Shift+Z on macOS)
Ctrl+S Save (Cmd+S on macOS)
Ctrl+Shift+S Save As… (Cmd+Shift+S on macOS)
Up / Down Previous / next motor unit
Ctrl+Up / Ctrl+Down Previous / next electrode grid or probe
Left / Right Previous / next spike during MUAP comparison; otherwise pan the source plot
[ / ] Previous / next spike during MUAP comparison
Home Reset the source and firing-rate view
Scroll Zoom time axis
Shift+Scroll Pan horizontally
Ctrl+Scroll Zoom both axes
P Open file notes to add post-its

File formats

Input

Format Notes
.otb+ OTBiolab+ archive (Quattrocento). EMG channels and auxiliary .sip channels (force, angle) are both supported.
.otb4 OTBiolab 4 archive (Novecento+). EMG grids, external/AUX channels, sampling rate, and acquisition metadata are discovered from the embedded track metadata.
.rhs Intan RHS2000 recording. Enabled amplifier channels (mV), board ADC inputs (V, as aux) and the sampling rate are read from the file header. Intan recordings are usually heavily oversampled relative to the amplifier bandwidth; use Decimate by (e.g. 5 for 20 kHz → 4 kHz with a 500 Hz band limit).
.mat MATLAB v5 and v7.3 (HDF5-based). Nested arrays can be selected with Inspect arrays… using paths such as recording.signal.
.h5, .hdf5 HDF5. Numeric datasets can be selected with Inspect arrays…; portable SCD HDF5 files are recognised automatically.
.npy NumPy array, shape (channels, samples) or (samples, channels) — the longer axis is assumed to be time.
.csv, .txt Numeric samples × channels table. CSV accepts one header row; text is whitespace-delimited.
.pkl (Edition tab) SCD Studio files, including SCD Edition 0.1.x files, and raw output of the swarm-contrastive-decomposition package (scd.save_results), which is converted on load.

Importing an unfamiliar format

For an unfamiliar MATLAB, HDF5, NumPy, CSV, or text layout, select it with Choose recording…. The inspector opens automatically when the selected format cannot locate an EMG matrix; it can also be opened manually with Inspect arrays…. Save the finished configuration as a reusable JSON profile. For a proprietary acquisition format, decode it with the vendor SDK and write a portable, sample-first .scd.h5 file with scd_app.io.write_portable_recording. The schema, conversion example, and verification checklist are in the data-import guide. Loader profiles never execute file-supplied Python code.

Output

.pkl files are standard Python pickle files. They can be opened in Python with:

Only do this with a file from a trusted source.

import pickle

with open("my_decomp.pkl", "rb") as f:
    data = pickle.load(f)

# Key fields:
data["ports"]  # list of port names (one per electrode grid)
data["discharge_times"]  # list[list[np.ndarray]] — spike timestamps in samples
data["sampling_rate"]  # int — sampling frequency in Hz
data["data"]  # np.ndarray — raw EMG, shape (channels, samples)
data["aux_channels"]  # list of dicts — force/aux data and metadata

Force channel setup

Force channels allow you to overlay the force trace on source signal and discharge rate plots, and to normalise force to %MVC.

Step 1 — Find the MVC value

The MVC value you enter must be in the same units as the force signal stored in the file.

For OTBiolab+ recordings (.otb+): The Quattrocento ADC stores force channels internally in millivolts (mV) after its analogue-to-digital conversion. OTBiolab+ displays force in Volts on screen (e.g. "MVC = 0.049 V"). To get the correct value for the config:

MVC in mV = OTBiolab+ displayed value × 1000

Example: if OTBiolab+ shows MVC = 0.049 V for Middle Extension → enter 49 in the config.

You can find the displayed MVC value by opening the .otb+ file in OTBiolab+ and reading the scale shown next to the force channel.

For Intan recordings (.rhs): board ADC inputs are converted to Volts (312.5 µV/bit); enter MVC in Volts.

For Novecento+ recordings (.otb4): external/AUX channels are converted using their per-track ADC metadata and retain the unit declared in the file (the supplied Novecento+ examples declare Volts). Enter MVC in that same unit.

For other formats: use whatever MVC value is in the same units as the raw signal values in your file. You can check what the signal amplitude looks like by loading a decomposition and reading the console output — when data loads, the application prints the force channel min, max, and net amplitude so you can verify the units.

Step 2 — Add the channel in the config

In Tab 1, click + Add Aux Channel and fill in:

  • Name — descriptive label (e.g. Middle Ext)
  • Unit label — must match exactly if you want auto-selection by filename (e.g. Middle Ext will be auto-enabled for files with mvc-15ext_fing-M in the name)
  • Source — Signal (channel embedded in the EMG array) or Auxiliary stream (OTB+ .sip or Novecento+ external/AUX track)
  • Channel start / end — 0-based index of the force channel
  • MVC (mV) — value from Step 1

Step 3 — Verify

After running decomposition, switch to Tab 4 and open the DR vs Force sub-tab. The right y-axis should show percentages consistent with the task (e.g. 0–20% for a 15% MVC contraction). If the values look wrong (e.g. showing 10,000%), check that the MVC value is in mV, not in Volts.


Under development

  • Multi-file comparison — loading two decompositions side by side for the same recording
  • Calibrated quality control — SCD-specific acceptance thresholds validated against independent reference decompositions

Citation

If you use SCD Studio, please cite the software itself. The DOI will be added here after the first archived release; until then, use:

@software{grison_scd_studio_2026,
  author = {Grison, Agnese},
  title = {SCD Studio},
  version = {0.2.0},
  year = {2026},
  url = {https://github.com/AgneGris/scd-studio}
}

The decomposition method is described in:

@article{grison2024particle,
  title={A particle swarm optimised independence estimator for blind source separation of neurophysiological time series},
  author={Grison, Agnese and Clarke, Alexander Kenneth and Muceli, Silvia and Ib{\'a}{\~n}ez, Jaime and Kundu, Aritra and Farina, Dario},
  journal={IEEE Transactions on Biomedical Engineering},
  volume={72},
  number={1},
  pages={227--237},
  year={2025},
  doi={10.1109/TBME.2024.3446806},
  publisher={IEEE}
}

@article{grison2025unlocking,
  title={Unlocking the full potential of high-density surface EMG: novel non-invasive high-yield motor unit decomposition},
  author={Grison, Agnese and Mendez Guerra, Irene and Clarke, Alexander Kenneth and Muceli, Silvia and Ib{\'a}{\~n}ez, Jaime and Farina, Dario},
  journal={The Journal of Physiology},
  volume={603},
  number={8},
  pages={2281--2300},
  year={2025},
  doi={10.1113/JP287913},
  publisher={Wiley Online Library}
}

Machine-readable citation metadata are available in CITATION.cff.

Diagnostics and logs

SCD Studio keeps a small set of rotating diagnostic logs on your computer. Use Help > Open Log Folder to find them, or Help > Copy Diagnostics to copy the software, Python, operating-system and compute-backend details needed for a useful bug report. Logs are limited to five 2 MB backups plus the active file.

Diagnostic logs can contain local file names and paths. Review them before sharing and never include participant recordings or identifying metadata in a public issue.

Contributing

Bug reports, format requests and code contributions are welcome. See CONTRIBUTING.md for the development setup, test commands and the information to include in a useful report.

The optional paper-style four-panel figure is generated from the real files in examples/scd-demo:

uv run python scripts/capture_demo.py --overview-only

It writes docs/figures/scd-studio-tabs.png. The PNG is intentionally gitignored; the capture code and example data are its reproducible source.

License

SCD Studio is open-source software licensed under the BSD 3-Clause License.

Third-party packages, including PySide6 and Qt, retain their own licences. Vendored code and example-data provenance are listed in THIRD_PARTY_NOTICES.md.

Contact

Agnese Grison
agnese.grison@outlook.it

Metadata

Release files for scd-studio 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scd-studio 0.2.0
File Size Uploaded
scd_studio-0.2.0.tar.gz 23.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for scd-studio 0.2.0
File Interpreter ABI Platform
scd_studio-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.7 MB

Release files / scd_studio-0.2.0.tar.gz

Download URL scd_studio-0.2.0.tar.gz
Size 23.4 MB
Tags Source
SHA-256 checksum
How to use checksums
62eb846dc8963d88acdc14ce941f23727d900026caf28ab9e36b124d2918dd10
BLAKE2b-256 checksum
How to use checksums
506958536e87a10442b465206cd6fab88b64bfda1da855ddaa536bb2208ce2f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / scd_studio-0.2.0-py3-none-any.whl

Download URL scd_studio-0.2.0-py3-none-any.whl
Size 18.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
62b74a951b5ad00e996e0879d3e0b9b6db6660c9387d834b867afe278226f3f3
BLAKE2b-256 checksum
How to use checksums
8c91665d0f984e10328148e6a9f4f2bafe966a57b8872bae6f67dfcb4cc5393b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page