Integrations
Live feed demo
WebSocket server and feedback nodes Supported; Chrome extension Beta.
The demo on this page is a simulator. It runs in your browser and makes up its own values. It uses no Studio, no device and no real data, and the moving values mean nothing. Its purpose is to show the format of the feed.
neuromore Studio runs a WebSocket server, by default on port 4547. While a session runs, it streams the value of every feedback node in the active classifier, such as Custom Feedback, Volume Control and Brightness Control, to all connected clients. The Chrome extension uses this feed to change the volume of audio and video elements and the brightness of the page.
The simulator builds its frames in the same format as Studio, then decodes them the way the extension does.
Simulated Studio is sending made-up values every 100 ms. No Studio, device or real data is involved.
Feedback nodes
Latest binary frame per node
Layout message (JSON)
waiting for ON_FEEDBACK_LAYOUT
Message sequence
Section titled “Message sequence”- A client connects to
ws://127.0.0.1:4547. - When a session starts, Studio sends
ON_SESSION_STARTED, thenON_FEEDBACK_LAYOUT. - Every 100 ms Studio sends one binary frame per feedback node.
- When the session stops, Studio sends
ON_SESSION_STOPPEDand the frames stop.
All JSON messages share one envelope: {"header": {"type": "..."}, "data": {...}}.
Connecting late works. A client that connects during a running session receives ON_SESSION_STARTED and the layout straight away, so there is no need to restart the session.
Studio sends those two messages to every connected client, not only the new one. So:
- Accept the layout at any time and replace your copy.
- Don’t treat
ON_SESSION_STARTEDas the start of a new session. A repeated message carries the original start time.
Pausing a session sends no message, and frames keep arriving. A client can’t see a pause in the feed. Don’t rely on the values either freezing or changing during a pause.
Clients also receive other messages. On connect, Studio sends ON_CLASSIFIER_LOADED, ON_STATEMACHINE_LOADED and ON_EXPERIENCE_LOADED for whichever of them is active. A state machine can also trigger OPEN_URL and BROWSER_* messages while a session runs. Ignore any type you don’t use. The Chrome extension guide lists the messages.
The layout message
Section titled “The layout message”data.feedbacks is an array with one entry per feedback node, sorted by the node’s vertical position in the graph, top first. The position in this array is the node index used by the binary frames, so a client must keep the layout to know which node a frame belongs to.
Each entry has three fields:
| Field | Meaning |
|---|---|
name |
The node’s name. |
sample_rate |
Set by the node’s Resolution attribute: High 30 (the default), Mid 10, Low 4, Original the rate of the input channel. This is not the frame rate. Frames always come every 100 ms. |
channels |
The names of the node’s input channels. They come from whatever feeds the node, not from the node’s name. The simulator uses Fz as an example. |
A node with no input connected stays in the layout, with empty channels and a sample_rate of 0, but sends no frames. The node indexes you receive can therefore have gaps.
The binary frame
Section titled “The binary frame”| Bytes | Type | Meaning |
|---|---|---|
| 0 to 1 | uint16, little endian | Message type. 1 means custom feedback. |
| 2 to 3 | uint16, little endian | Node index into the layout’s feedbacks array. |
| 4 onward | float64, little endian, 8 bytes each | One value per input channel. |
A node with one channel produces a 12 byte frame. The value 0.5 is encoded as 00 00 00 00 00 00 e0 3f.
Every channel slot carries the same number: the node’s current value. Treat the first value as the feedback value.
Decoding in JavaScript
Section titled “Decoding in JavaScript”Set binaryType to arraybuffer and pass true to every DataView read. Without that flag DataView reads big endian and the numbers come out wrong.
const ws = new WebSocket('ws://127.0.0.1:4547');ws.binaryType = 'arraybuffer';let layout = [];
ws.onmessage = ({ data }) => { if (typeof data === 'string') { const msg = JSON.parse(data); // The layout can arrive at any time. Other message types are ignored. if (msg.header.type === 'ON_FEEDBACK_LAYOUT') layout = msg.data.feedbacks; return; } const view = new DataView(data); if (view.getUint16(0, true) !== 1) return; const index = view.getUint16(2, true); const values = []; for (let i = 4; i + 8 <= data.byteLength; i += 8) values.push(view.getFloat64(i, true)); console.log(layout[index]?.name ?? `node ${index}`, values[0]);};What the Chrome extension does with the values
Section titled “What the Chrome extension does with the values”The extension reacts to two node names. Use the Volume Control and Brightness Control nodes from the Output category of the palette and keep their names.
- Volume Control sets the volume of the page’s
audioandvideoelements to the value. - Brightness Control sets the brightness of the page with a CSS filter. 1 leaves the page unchanged and 0 makes it black. On YouTube the filter goes on the video player container only.
Both nodes expect a value from 0 to 1, and it is up to your classifier to stay in that range. Studio doesn’t limit the value: the node shows “Value not in range” and the raw value is still sent. The simulator’s meter bars stop at 0 and 1, but the number beside each bar is the value as received.
See Browser neurofeedback for how these nodes fit into a classifier, and Browser constraints for limits on specific pages and players.
Connect to your own Studio
Section titled “Connect to your own Studio”To replace the simulation with your real nodes:
- Start Studio on the same computer as this browser.
- Load a classifier with at least one feedback node, and start a session with the Begin Session button. See Running a session. You can also do this step after step 3.
- Press Connect to my Studio in the demo.
The demo only reads the feed. It sends nothing to Studio, and the values stay in your browser.
This can fail. The demo then says so and goes back to the simulator. The usual causes are:
- Studio is not running, or another program uses port 4547.
- The port was changed in NMStudio > Settings > Network > Websocket Server > Listen TCP Port. The demo and the Chrome extension always connect to 4547. Set it back and restart Studio.
- Your browser blocked the connection. Browsers can stop a public https page from connecting to your own computer. Some refuse outright and others ask for permission to access the local network. This depends on the browser and its version.
If the connection works but no nodes appear, no session is running.
A local app is not a web page, so this browser restriction doesn’t apply to it. The Chrome extension connects from inside the browser tab, so the restriction can affect it too.
Caution: The WebSocket interface is an unauthenticated local protocol. Keep its port (4547 by default) firewalled and don’t expose it to untrusted networks.
Next steps
Section titled “Next steps”- Browser test workflow: test the whole path with the Chrome extension.
- Chrome extension guide: install the extension and see the full message table.