Skip to content

The experiment editor ​

An experiment is a test written as a graph: nodes that send (an HTTP request, an OSC message, an MQTT publish…), wait for an answer, check what came back, play the other side or break the network on cue, joined by wires that say what runs next. A run starts at Start, follows the wires and passes when it reaches End with every step passed. You build it on the Experiments screen, run it there, and run the same file from the command line or a server.

The editor holds one experiment at a time and saves it as you work. To keep several, export snapshots or open another from a file (see Saving and files).

Every kind of node, its fields and outputs are in the node reference. How values flow between nodes is Data and templates; parallel branches, loops, retries and repeats are Flow.

The screen ​

AreaWhat it holds
Toolbar (top)The experiment's name and save state, adding nodes, parameters, profiles, the panes, focus mode, fullscreen and Run
Canvas barUndo and redo, the node finder, Arrange and zoom
CanvasThe graph: nodes, wires, and the run's progress drawn on them
Properties (right)The selected node's fields, its preview and Send now; or what several selected nodes or a selected wire allow
Run timeline (bottom)The steps of the current or last run, its outcome, report and seed

The properties pane and the timeline have a handle on their inner edge: drag it, or focus it with Tab and use the arrow keys (Shift for bigger steps); a double click or Enter gives the pane its default size back. Sizes are kept for the next time. The timeline stays folded until the first run opens it.

The toolbar ​

ControlWhat it does
☰ ExperimentsOpens the templates, opening a JSON file and exporting (Saving and files)
Experiment nameThe experiment's name; a run needs one. Run reports record it, and Compare finds earlier runs by it
Saved / Saving… / Save failedWhether the last change is on disk
⚠ Complete the graphShown while the experiment cannot run; its tooltip says why, a click selects the node it is about (Validation)
+ Add nodeOpens the add menu, after the selected node when there is one (A)
ProfileWhich profile runs, the preview and Send now use; shown when the experiment has profiles. ⚠ marks a profile that would not run
{ } ParametersParameters, profiles, the seed, cookies and secrets (Data and templates)
☷ PropertiesShows or hides the properties pane
▢ Focus editorHides the sidebar, the header and the bottom panel (Focus mode and fullscreen)
⛶ FullscreenThe window in fullscreen, with focus mode
Run experimentChecks, saves and runs the experiment; while it runs, the button is Stop
▾ Run with…One run with another profile, other parameter values or a given seed, without changing the experiment

Moving around the canvas ​

  • Pan: drag the empty canvas, or scroll.
  • Zoom: Ctrl and the mouse wheel zoom around the pointer; − and + in the canvas bar step by 10 %. Zoom goes from 15 % to 200 %; the percentage between the buttons shows where you are.
  • 1:1 (Reset zoom, Ctrl+1) goes back to 100 %.
  • ⊡ Fit graph (Ctrl+0) shows the whole graph, at 100 % at most.
  • Find a node: Nodes in the canvas bar (it shows how many nodes there are) or Ctrl+F. Type words from the node's name, its summary (a URL, an address, a topic) or its id; ↑↓ choose, Enter selects the node and brings it into view (zoomed to at least 80 %), Esc closes.

There is no minimap: Fit graph and the finder do its work.

Nodes and wires ​

A node shows its kind, a one-line summary of what it does, and badges for its settings: ↻ and a number for Retry, × and a count (or a time) for Repeat, ⚡ for Load. Its input is on the left — every node but Start has one — and its outputs on the right:

OutputOnFollowed when
OutputMost nodesThe step passed
Yes / NoStatus branch, Branch on valueThe comparison held / did not
Branch 1 / Branch 2Parallel branchAlways, both at once
Matched / TimeoutWaitsA matching message arrived / none did in time
Body / Done / LimitLoopAnother iteration / the loop is over / it ran out of iterations

An output may have several wires. The first wire continues the branch; each further wire starts a parallel branch with a copy of the variables known at that point. A Join branches node waits for every wire that leads into it. Details are in Flow.

Timeout and Limit are optional: left unwired, a timeout or running out of iterations fails the step. Every other output must have a wire before the experiment can run.

The wire from the body of a Loop back to it is drawn as an arc over the body; it is the only wire that may lead backwards.

Adding nodes ​

The add menu ​

The add menu lists every kind of node by group — Actions, Observe, Emulate, Faults, Data, Checks, Flow — and then Saved signals: the signals of your library that can become a node (OSC, HTTP, UDP with a text payload, MQTT), with their fields filled in.

Its search field has the focus as it opens. Type a few letters: every word must appear in the node's name, its description or its type (http, wait_osc); a signal is found by its name, folder, transport or target. ↑ ↓ choose, Enter adds, Esc closes. The line at the top says where the node goes: after a node, or parallel to one.

A new node is selected, the properties pane opens, and its main field — the URL, the address, the topic, the delay — has the focus with its text selected, so you can type straight away. Esc in a field takes you back to the node on the canvas, ready for the next A.

After the selected node ​

A, + Add node in the toolbar and + Add next in the properties pane add the next step after the selected node:

  • It attaches to the node's first output that has no wire yet (the free No of a Status branch, say), or else to its first output.
  • If that output already has a wire, the new node is spliced into it (into the first, if it has several): the wire now runs through the new node, and everything after it moves right to make room.
  • With End selected, the node goes before it, when one wire leads into it.
  • With nothing selected, the node lands in the middle of the view, without wires.

A node with a single output that is put on the empty Body of a Loop — this way or on a new wire — is also wired back to it, so the body is complete at once.

Into a wire ​

Every wire has a + in its middle (Insert node). It opens the add menu, and the node you pick is spliced into that wire. A Loop spliced in this way continues the flow through its Done.

On a new wire ​

Drag a wire out of an output and let go on the empty canvas: the add menu opens there, and the new node goes on a new wire of that output — beside the wires it already has, so it runs in parallel with them. The same happens when you click an output and then click the empty canvas, or double-click it.

Anywhere ​

Double-click the empty canvas to add a node at that spot, without wires.

From other screens ​

  • Add to experiment on the OSC and HTTP screens adds what you just tried as the next step — just before End, when one wire leads into it — and switches to the editor.
  • Wait for this next to a message in the OSC monitor adds a Wait for OSC that recognises it: its address and its text, whole-number and true/false arguments (floats are measurements that change, so they are left out). Stop the monitor before running: the run listens on that port itself.
  • Wait for this on a topic of the MQTT tree adds a Wait for MQTT on that topic, at that broker.

While a run is going, nodes cannot be added; the console says so.

Connecting nodes ​

  • Drag from an output onto a node. Letting go within 24 pixels of a node is enough.
  • Click an output (or focus it and press Enter or Space): the canvas bar says Choose an input port. Click a node or its input to connect; click the empty canvas to add a node there on a new wire; Cancel or Esc gives up.

A wire is refused when it would create a cycle (other than the way back of a Loop), when it leads into Start or out of End, or when it would connect a node to itself; the editor says so. Drawing a wire that exists already changes nothing.

To remove a wire, click it — the properties pane shows what it connects — and press Delete, or use Remove connection there. Hovering a wire also shows a × above its +. The properties of a node list its outgoing wires, each with × (Disconnect).

Selecting several nodes ​

ToDo
Select a nodeClick it, or move to it with Tab
Add a node to the selection, or take it outShift or Ctrl and a click
Select with a frameShift and drag on the empty canvas: every node the frame touches joins the selection
Select every nodeCtrl+A
Clear the selectionClick the empty canvas; Esc when several are selected
Move themDrag one of them: they all move
Nudge themWith a node focused, the arrow keys move the selection 5 pixels, 20 with Shift

The properties pane shows the last node picked; with several selected, it shows how many, with Copy, Duplicate and Delete nodes.

Copy, cut and paste ​

Ctrl+C copies the selected nodes and the wires between them as text; Ctrl+X also removes them; Ctrl+V pastes them — into this experiment, into another one opened later, or into another Signal Lab window. The text is JSON, so you can also keep it in a file or a message.

  • Start and End are one of a kind: they are never copied.
  • Wires between a copied node and the rest of the graph are not copied; wire the copy in yourself.
  • Every pasted node gets a new id.
  • A node that names another one — Change impairment, Emulator down/up, WebSocket send, Wait for WebSocket, WebSocket close — names the copy when that was copied too; otherwise it keeps naming the original if it is in this experiment, or the first node of that kind here.
  • A pasted Emulator or Impairment whose port this experiment already listens on moves to the next free port. What sends to the original still does.
  • The copy lands 32 pixels right of and one row below where it came from, further down until it covers no node, and is selected.

Ctrl+D (Duplicate) does the same without the clipboard.

Deleting ​

Delete or Backspace (Delete node, Delete nodes) removes the selected nodes and their wires. A node that had exactly one wire in and one wire out leaves a wire in its place, from the node before it to the node after it, so a chain stays connected. Start and End cannot be deleted.

Undo and redo ​

Ctrl+Z undoes; Ctrl+Shift+Z or Ctrl+Y redoes (↶ ↷ in the canvas bar). A drag, a series of arrow-key nudges or the typing in one field is one step. The last 100 steps are kept while the app is open, across screen switches; opening another experiment is one step too, so Ctrl+Z brings the previous one back. Undo and redo wait while a run is going.

Arrange ​

Arrange lays the graph out from left to right: each node in the column after the last node that leads to it, the body of a Loop in its row — and then fits the view. It is one step of the history. A draft with a cycle that is not a loop's is left as it is.

The properties pane ​

With one node selected, the pane shows, from top to bottom:

  1. The node's name (its tooltip says what it does) and, when the experiment cannot run because of this node, what is wrong.
  2. Its fields. Fields that take templates suggest parameters, variables, secrets and generators as you type {{, or on Ctrl+Space.
  3. Its settings: send under load on an HTTP request, repeat sending on nodes that send, retry on failure on nodes that send or listen, and wait for a reply on OSC and UDP messages (see Node settings). Load replaces Repeat and Retry while it is on.
  4. The last run's load result, on an HTTP node that ran under load.
  5. The preview — Will be sent, Will wait for or Will compare — when the node has templates: what it would send, wait for or compare, resolved with the current parameters and the values known so far (Send now and the preview).
  6. Send now or Listen now, and what it did last.
  7. Its outgoing wires, each with ×.
  8. ⚡ Route through impairment on an OSC or UDP message: an Impairment is put in front of the node — listening on a free loopback port from 9010 up, forwarding to the node's target — and the node is pointed at it, so the next run degrades what it sends (Faults).
  9. + Add next, Copy, Duplicate and Delete node.

A double click on a node opens the pane with the cursor in its main field. With several nodes selected the pane offers what can be done to all of them; with a wire selected, what it connects and Remove connection. While a run is going, the fields are locked.

Send now and the preview ​

Send now (Ctrl+Enter, also from inside the node's fields) sends the selected node on its own, without running the experiment, through the same code a run uses. It is offered on HTTP request, TCP message, OSC message, UDP datagram, MQTT publish, WebSocket connect and WebSocket send. On a wait it is Listen now: the wait listens from now until a message matches or its timeout ends.

  • The node uses the active profile, the stored secrets and the variable values known so far — from the last run and from earlier Send now results. If a template names a value nobody has set yet, nothing is sent and the names are listed.
  • It is sent once: Retry, Repeat and Load do not apply, no cookies are kept, and emulators and relays of the experiment are not started.
  • A WebSocket send or Wait for WebSocket opens the connection its WebSocket connect describes, for that one test.
  • On an HTTP request the response is shown — status, time, size, and the body, JSON formatted. The values the Extract value nodes after the request would take are filled in at once. Click a value in a JSON response to add an Extract value node for it right after the request, its variable named and its value known. Mock this turns the response into a route of an emulator.
  • The result is also written to the console.

The preview is resolved by the engine as well, about a quarter of a second after a change. Secret values are never shown, there or anywhere.

Validation ​

The editor checks the experiment as you edit, and once more when you press Run experiment:

  • An output that still needs a wire pulses amber.
  • A node no wire from Start reaches is drawn dashed; its tooltip says so.
  • The node a problem is about is outlined, and the problem is written above its fields.
  • ⚠ Complete the graph in the toolbar names the problem; a click selects the node.

An experiment runs when, among other things:

  • it has a name, exactly one Start and one End, and at most 64 nodes;
  • every node is reachable from Start and every required output has a wire;
  • the only cycles are the bodies of Loop nodes leading back to them;
  • every check and Extract value has an HTTP request before it on every path (one under load does not count: it leaves no response);
  • a WebSocket send, Wait for WebSocket or WebSocket close comes after the WebSocket connect it uses;
  • every field is filled in and in range, and every template names something known at that point.

Unfinished drafts are saved all the same. A run is refused, before any traffic, when a secret it needs is not stored. Every message is listed in the error reference.

Running ​

Run experiment checks the experiment, saves it, and starts it. If it cannot run, the problem is shown and its node selected. Otherwise the timeline opens and nodes light up as the run reaches them: ● running, ✓ passed, ✕ failed, ↻ retrying, ⟳ repeating, ⚡ under load; wires that carried the flow are coloured too.

  • Waits, emulators and impairment relays open their ports before the first step, so nothing that arrives early is missed. A port that cannot be opened (another program has it, say) keeps the run from starting, and the node that needs it is shown.
  • Stop ends the run at once: pauses, waits and loads included. The run is also a job in the console's job strip, which can stop it too.
  • A run that takes longer than 300 seconds is stopped and fails.
  • While a run is going the experiment cannot be edited; panning, zooming and selecting still work.

▾ Run with… next to the button runs once with another profile, other parameter values or a given seed; the experiment itself is not changed, and the form keeps what you typed until the app closes (Data and templates).

The run timeline ​

The Run timeline lists one row per step: the time, the node and what happened — passed, failed and why, a retry, a repeat's progress, a load's numbers once a second. Click a row to select its node on the canvas.

Its title line holds:

  • the outcome: Passed, Failed with the reason, or Stopped;
  • Report saved — the run's report file (on a server, a download);
  • Compare — this run beside an earlier one of the same experiment;
  • the profile and changed values the run used, when it had any;
  • the run's seed, with Pin to keep it in the experiment so the next runs draw the same random values, or Unpin once it is pinned.

When the Inspector was capturing, a wait (or an expected reply) that matched links to the frame it matched; a click opens it in the Inspector. Reports, seeds and comparing runs are in Runs and reports.

Focus mode and fullscreen ​

▢ Focus editor hides the sidebar, the header and the bottom panel (console and Inspector), leaving the screen to the editor. ⛶ Fullscreen puts the window in fullscreen and turns focus mode on; leaving fullscreen puts focus mode back the way it was. Switching to another screen leaves focus mode.

Esc on the canvas, with nothing else to close, leaves fullscreen and then focus mode.

Saving and files ​

The experiment is saved by itself, a moment after each change, to experiment.json in the data folder (Documents/SignalLab on a desktop; a server has its own — see Files and folders). Drafts that cannot run yet are saved too. A run saves first, so what ran is what is on disk.

If that file cannot be read — edited by hand into broken JSON, say — the editor says which file and why, and leaves it alone. Opening another experiment from ☰ Experiments then replaces it at the next save.

☰ Experiments opens the experiments dialog:

  • Templates: pick one and press Open experiment. They all use loopback addresses.
  • Open JSON… reads an experiment file — written by this version of Signal Lab or an earlier one, up to 4 MiB — and shows its name and how many nodes and connections it has before you open it. Files from earlier versions are brought up to date as they open. A file that does not parse, or would not be a valid experiment, is refused with the reason, and the current experiment stays.
  • Export current JSON writes a snapshot of the current experiment — nodes, wires, positions, parameters and profiles, never secret values — to a new file in exports in the data folder and shows its path; on a server, with a Download link.
  • Open experiment replaces the current experiment with the template or file. It does not run it, and Ctrl+Z brings the previous one back.
TemplateWhat it does
Empty experimentStart and End, for your own flow
HTTP checkA GET to http://127.0.0.1:8080/ and a check for status 200 — the experiment you start with
HTTP to OSCThe same request; on 200 an OSC message to 127.0.0.1:9000, otherwise a 500 ms delay
Parallel flowsTwo branches at once — a request and a log line — joined before End
OSC ping → replySends /ping with the run's id to 127.0.0.1:9000 and waits on 127.0.0.1:9001 for /pong carrying it back
Poll until readyAsks a device for /status every 0.3 s until it answers ready, ten times at most
Retry a flaky APIAn emulated API that fails twice before it answers, and a loop that asks until it does
Fault phasesDatagrams to an emulated device through an impairment relay while a parallel branch switches the network clean, lossy, offline and clean again
Dependency outageAn emulated API taken down for two seconds by a parallel branch, and a client that keeps asking until it answers again
WebSocket echoConnects to an echo service at ws://127.0.0.1:9001/echo, sends a JSON ping, expects it back unchanged, and closes

The same files run without the editor: signallab run experiment.json — see The command line.

Keyboard shortcuts ​

Single keys act on the canvas and are left alone while you type in a field. On a Mac, in a browser, Cmd works where Ctrl is written.

KeysAction
AAdd a node after the selected one, or mid-view when nothing is selected
Ctrl+EnterSend now, or Listen now, on the selected node
Ctrl+ZUndo
Ctrl+Shift+Z, Ctrl+YRedo
Ctrl+ASelect every node
Ctrl+C / Ctrl+X / Ctrl+VCopy / cut / paste the selected nodes and the wires between them
Ctrl+DDuplicate the selected nodes
Delete, BackspaceRemove the selected wire, or the selected nodes
Arrow keys (a node focused)Move the selected nodes by 5 pixels; with Shift, 20
Ctrl+FFind a node
Ctrl+0Fit the graph
Ctrl+1Zoom to 100 %
Ctrl + mouse wheelZoom around the pointer
Shift + click, Ctrl + clickAdd a node to the selection, or take it out
Shift + drag on the empty canvasSelect with a frame
Double-click a nodeEdit its fields
Double-click the empty canvasAdd a node there
Enter, Space on a focused outputStart a wire from it
{{ or Ctrl+Space in a fieldSuggest parameters, variables, secrets and generators
Esc in a fieldBack to the node on the canvas
Esc on the canvasClose the add menu; else cancel the wire being drawn; else let go of the selected wire; else clear a selection of several; else leave fullscreen; else leave focus mode

In the add menu and the finder, ↑ ↓ choose, Enter takes the choice and Esc closes. All of the app's shortcuts are in Keyboard shortcuts.