- JavaScript 89.4%
- HTML 8.7%
- PowerShell 1.1%
- Shell 0.7%
| deploy | ||
| lib | ||
| public | ||
| scripts | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| CHANGES.md | ||
| package.json | ||
| README.md | ||
| server.mjs | ||
| Start Matrix Control.cmd | ||
| Start-MatrixControl.ps1 | ||
Matrix Control
A local Q-SYS Delay Matrix and six-band parametric EQ controller with a compact matrix, input/output fader views, live EQ sweeping, channel search and hiding, groups, favourites and saved layouts. No npm packages or build step required.
Develop on another machine
Clone over HTTPS and run with Node.js 22 or newer:
git clone https://repo.calhta.com/calum/qsys-remote.git
cd qsys-remote
npm test
npm start
Open http://localhost:3210. No package installation or build step is required. With QSYS_HOST and QSYS_COMPONENT unset, the app starts in demo mode and needs no site access. For the isolated live-control simulator (including EQ), run node scripts/eq-ui-fixture.mjs and open http://localhost:3211. It never connects to the real Core.
npm test uses simulated connections. The separate manual commissioning script scripts/check-output64.mjs writes real controls when explicitly enabled; the old output-64 test permission is historical, so confirm its current use before running it on site.
The Debian service template contains site-specific paths and Core settings. Review them before installing it elsewhere. Source code does not include browser-local groups/layouts, live Core inventories, credentials, SSH keys, logs, or runtime archives. Group defaults are in public/app.js; DSP settings remain on the Core.
Current handover status — 20 September 2026
- Site controller: http://10.96.20.103:3210 (previously .104); Core: 10.96.20.2; matrix: REM30_DEL_MTRX.
- All 40 configured EQs were confirmed exposed and readable on 19 September, including EMP_BED_GRP.
- Matrix supports live gain/delay/fader sweeps and Ctrl-click multiselect; knob moves preserve offsets, numeric entry sets equal values, Esc clears selection.
- EQ areas: Entertainment, Emperor, Guest, Lift, Corridor, SPA, Pool. Live sweeps, fullscreen and pinch-Q are implemented; physical multitouch validation remains outstanding.
- Pool Deck end-to-end patch is not confirmed. Last read-only check found input 15 (POOL) at −100 dB to all 64 outputs. Do not infer output/Dante assignments from the cropped screenshot.
Debian deployment (active)
- Controller: http://10.96.20.103:3210
- Server: Debian 13, SSH user
root, project key atC:/Users/calum/.ssh/qsys-matrix-controller_ed25519on the development laptop. The private key is not deployed. - Application:
/opt/qsys-matrix-controller; official Node.js 24.21.0 runtime:/opt/qsys-node. - Service:
matrix-control.service, enabled at boot, running with a non-root dynamic user. It readsREM30_DEL_MTRXon Core10.96.20.2on startup; it never replays audio values. - Inspect:
systemctl status matrix-control; logs:journalctl -u matrix-control; restart:systemctl restart matrix-control. - Q-SYS/management NIC:
ens18, DHCP address10.96.20.104/24. - Internet NIC:
ens19, static192.168.100.20/24, gateway192.168.100.1, DNS202.175.36.16. Persistent configuration is in/etc/network/interfaces.d/ens19-internet;/etc/resolv.conf.headkeeps this DNS first on DHCP renewal. - Verified from the development laptop: HTTP 200, live connection, 24 inputs, 64 outputs, all 1,536 crosspoints. All automated tests passed on Debian. Outbound HTTPS also passed after the new NIC was configured. No live audio values were written during deployment.
The new server URL has its own browser-local layout storage. Groups from the old localhost URL do not automatically move to this address or another laptop.
Start on Windows
Double-click Start Matrix Control.cmd. It starts a hidden local Node process and opens your browser at http://127.0.0.1:3210. Running the launcher again opens the existing app. Logs are in this folder. Alternatively, run node server.mjs in a terminal (Ctrl+C stops it).
Requires Node.js 22 or newer. The launcher also finds the bundled Codex Node runtime on this computer. The server listens on IPv4 network interfaces as well as loopback. Set the HOST environment variable to 127.0.0.1 to restrict it to this computer.
Access from another laptop
Use the active Debian controller at http://10.96.20.103:3210/ from a laptop that can reach that network. The Debian service starts automatically at boot; the development laptop does not need to remain running.
There is no app login, so devices permitted to reach this port can operate the matrix. The Core connection is shared across laptops; layouts and groups remain browser-local and do not transfer automatically. Opening the IP URL also uses separate browser storage from the localhost URL. Group creation works over plain HTTP LAN addresses.
Connect to Q-SYS
- Select the Delay Matrix component in Designer. Set Script Access to External, or All if scripts also use it. Note its Code Name. No individual Named Controls are required.
- Make that component configuration available in the running design. Deploy design changes at a suitable time for your system.
- Open the app's gear icon. Enter the Core IP (prefilled with
10.96.20.2), QRC port (normally1710), and credentials if required. Passwords are never stored. - Click Find matrices, select the component, then Open matrix. Discovery reads values only. For local Designer Emulate mode use
127.0.0.1.
The first run starts in Demo mode, clearly labelled. Demo edits never go to a Core. The app never uploads demo values, layouts or stored audio presets on connection. A reconnect requires selecting the component and reading its current controls again.
Working with the matrix
- Search inputs and outputs separately, or press
/to search inputs. - Channels chooses what is visible. Show all channels clears searches and visibility filters.
- Hide unused hides rows/columns with no above-minimum-gain crosspoints in the current view. Muted routes with a configured gain remain visible, so they can still be unmuted.
- Select an output row or a crosspoint. Adjust its sends with faders, numeric gain, delay in milliseconds, or mute. Faders stream values while dragged; numeric fields commit on Enter or blur.
- Output mixer gives the faders the working area. Input sends shows the destinations of one input. Arrow buttons page through banks of twelve.
- Add/edit groups in the sidebar. Groups select both input and output membership (range entry is supported); they do not link gains or delays. Existing output-only groups retain all inputs. Selecting a group clears the previous view filters.
- Pin an entire output or individual sends. Favourites respects your current group, searches and channel visibility; clear filters if something seems missing.
- Saved views store filters and channel selection, not audio settings. Layouts are stored in your browser separately for demo and each Core/design/component.
- All controls provides searchable access to the discovered control inventory, including optional input gain/mute and labels. Delay values in that diagnostic view are raw seconds, clearly indicated. Inventory can be exported as JSON.
Connection behaviour and scope
- QRC JSON-RPC over TCP port 1710.
Component.GetComponentsandComponent.GetControlsdiscover components and metadata; Matrix edits useComponent.Setfor one identified control followed byComponent.Getconfirmation. EQ gestures send batches of up to two controls through an acknowledged queue, with full readback when movement settles. - Change-group polling at 300 ms reads external changes. Controls are disabled if the Core is disconnected, standby, or its state is unknown. A failed/unconfirmed write disconnects the session; no queued edits are replayed. Read current values by reconnecting.
- Automatic matrix mapping recognises Q-SYS 10.4.1
input.N.gain|delay|mute.Mnames, verified against REM30_DEL_MTRX (24 × 64, 7,952 exposed controls). It also accepts explicitinput.N.output.M.gain|delay|muteand reversedoutput.M.input.N...names. Unknown schemes remain available in All controls, rather than guessing crosspoint orientation. - Matrix gain uses the component's numeric limits; standard gain is shown in dB and nonstandard gain as raw values. Delay fields convert milliseconds to seconds for QRC. Missing/read-only controls cannot be edited.
- No DSP processing, audio metering, hardware fader integration, linked groups, audio snapshot recall, or automatic redundant Core failover in this first version. Design-time properties such as maximum delay remain in Designer.
- This browser app has no remote dependencies or app login. Network access is for your trusted laptops; HTTP requests accept this computer's IPv4 addresses/hostname and require matching origins for browser writes.
Deploying another Debian instance
Copy this folder to a Debian machine with Node.js 22+, run node server.mjs, then open http://<debian-IP>:3210. Alternatively run HOST=127.0.0.1 node server.mjs and use an SSH tunnel from your laptop: ssh -L 3210:127.0.0.1:3210 user@debian-host, then open http://127.0.0.1:3210 on the laptop. The Debian machine needs network access to the Q-SYS Core. No source changes are needed.
Verification
Run node --test. Tests cover QRC framing/errors, matrix orientation and inventory, value validation, read-only connection setup, confirmed writes, delay seconds, standby/disconnection, stale sessions, and HTTP origin checks using a simulated Core. Real audio operation must be checked against your actual matrix.
Verified on 2026-09-15 against the user's Core 24f at 10.96.20.2, component REM30_DEL_MTRX: discovery, all 1,536 crosspoints, labels, feedback and confirmed writes. Only input 24 → output 64 was used for live write tests, with user permission. Delay 0 → 12.5 ms → 0, gain −100 → −90 dB → −100, and mute off → on → off were verified and restored. Outputs 1–62 were not targeted by any test write. Browser checks covered searching, gain/delay/mute in demo, group creation, saved view persistence, settings discovery and the live output view. This does not constitute an audio-system commissioning test.
References: QRC commands, Delay Matrix controls and units.
September 16 interface update
- Double-click matrix input/output headings or fader-strip names to rename a channel; OK writes the label, Cancel/ESC does not. The pencil beside the selected channel opens the same dialog.
- Twelve faders per bank across desktop screens. Smaller screens wrap the bank into rows. Drag to adjust gain; a single click does not jump the gain. Double-click a gain fader to set 0 dB.
- Group editor includes separate input/output checklists and range entry, e.g. 5-9 and 23-40. Existing group IDs and saved layout storage are preserved.
- EQs has a separate tab with working six-band controls, response preview and group assignments (one component Code Name per line). See the EQ sections below.
- Tested in isolated demo: confirmed/cancelled renaming, reset to 0, twelve-column layout, input/output ranges, persistence and EQ view isolation. No live audio values were changed for this update.
Matrix touch-and-turn view
The Matrix tab uses inputs across the top and outputs down the left. Choose 8 outputs × 8 inputs or 12 outputs × 24 inputs, with independent paging; custom group membership and searches apply before paging. Select a cell, then drag the large right-hand knob vertically, or enter gain/delay numerically. The Gain/Delay switch changes both the grid and the knob. Shift-drag is finer; arrow keys also work. The knob streams changes while dragged and confirms the final position on release; selecting a crosspoint does not write audio. Double-click the gain knob for 0 dB. Mute mode replaces rotary controls with regular mute buttons. The Output mixer and Input sends tabs retain twelve-fader desktop banks.
Entertainment EQs
Open EQs, then 001 Entertainment, and choose a component. All eleven provided ENT_* EQs are assigned automatically once per browser/design. Edit six bands and master controls; values refresh every two seconds while not editing. Groups can contain only EQ assignments or include matrix input/output memberships. EQ settings live on the Core; group assignments remain browser-local. Q is editable by knob, numeric entry, mouse wheel on a band point, or two-finger graph pinch; writes convert to the Core’s bandwidth control. Additional exposed parametric EQs can be assigned by Code Name in the group editor.
Graphical EQ controls
The EQ editor now has a response graph with draggable colored band handles, All bands and Phase toggles, Gain, Frequency and Q knobs arranged vertically with numeric entries to their right. Shift-drag provides fine control. Knob and graph movement streams live; numeric edits commit on Enter or leaving the field. Escape ends movement at the current live value without restoring earlier settings. Q is now editable via the Core's bandwidth control and is disabled for shelves. The graph is a calculated 48 kHz preview based on the W3C Audio EQ Cookbook, not a measured or Q-SYS-rendered trace. Exact matching of Q-SYS shelf curves and phase remains unverified. Control behavior follows the Q-SYS parametric EQ reference.
Live sweeping update
EQ knobs and graph points now send changes continuously during movement, at up to 25 updates/s subject to Core acknowledgement/network speed. Only the latest pending value is retained, and the release position is sent and read back. Controls remain responsive while updating. Escape or pointer cancellation stops at the current live value; it does not restore the pre-drag value. Matrix gain/delay knobs and faders also stream through an acknowledged queue, retaining the latest pending position.
Touch EQ bandwidth
Hold a parametric band point with one finger, then place a second finger anywhere on the graph. Spread to widen the band (lower Q), pinch to narrow it (higher Q). Frequency and gain stay fixed during the pinch. Lifting either finger ends the gesture. Q streams live through the same bounded write queue as other EQ controls. Shelf filters do not support Q. Physical touchscreen testing remains to be done.
Latest validation — 16 September 2026
All 13 automated tests passed locally and on Debian after deployment. Tests include live-write coalescing, final-position delivery, session cancellation, and pinch direction/bounds. The service remained active; this frontend update required no restart. Touch pointer integration still needs verification on a physical multitouch device. No live Core audio settings were changed during these EQ tests.
Live matrix and EQ fullscreen update
Matrix gain/delay knobs and mixer faders send during movement (up to 25 queue batches/s, subject to acknowledgement speed), with confirmed final values. Connection/session changes discard pending writes. Cancelling a gesture cannot undo values already sent. EQ refreshes preserve scroll position and do not replace focused numeric fields. EQ Fullscreen expands the module; use Exit fullscreen or Esc to return. Browser fullscreen is used where available, with an in-page fallback. Short-screen graph height adapts, and scrolling remains available where required.
Verified in the isolated simulator: intermediate gain, delay (seconds on the wire), and fader writes; final readbacks; scroll unchanged across repeated EQ refreshes at 1280×600; fullscreen controls fit at 1366×768; Exit/Esc and graph proportions. All 14 tests passed. No live Core audio values were changed.
Matrix multiselect
Ctrl-click (Cmd-click on Mac) adds/removes crosspoints; a plain click selects one. Esc clears the selection. Selected cells are highlighted and the inspector shows the count. Knob and arrow-key gain/delay changes are relative to each starting value; limits clamp each control individually. Numeric entry (and double-click gain reset) sets all selected values equally. The inspector Mute selection button mutes all, or unmutes all when all are muted. Ctrl-click in mute mode never toggles audio. Selection is temporary, cleared on workspace/session changes; filtered-out channels are removed. Paging and metric changes retain it. Larger selections update sequentially and can take longer.
Verified in an isolated simulator: live paired gain writes preserve a 3 dB offset; delay offsets preserve 10 ms difference; numeric delay entry sets both to 25 ms; shared mute, Ctrl-click without muting, and Esc clearing. All 16 automated tests pass. No live Core audio values changed.
EMP EQ additions — 19 September 2026
Nine component IDs configured: EMP_LOU_MIDHIGH, EMP_LOU_SUB, EMP_LOU_GRP, EMP_BED_MIDHIGH, EMP_BED_SUB, EMP_BED_GRP, EMP_BATH_MIDHIGH, EMP_BATH_SUB, EMP_BATH_GRP. An EQ-only EMP group is seeded once per browser/design when any of these components is discovered; later user edits/removal are preserved. Existing Entertainment assignments remain unchanged. Uses the existing EQ controls and live sweep behaviour. Deployment and live inventory verification are pending: .103 app responds but reports Core disconnected, and the project SSH key is rejected. .104 does not respond.
Lift and Corridor EQs — 19 September 2026 (local, deployment pending)
Added LIFT_MIDHIGH, LIFT_SUB, LIFT_GRP and CORR_MIDHIGH, CORR_SUB, CORR_GRP. Lift and Corridor EQ-only groups seed independently once their components are discovered, alongside EMP and existing Entertainment. All 15 newly requested EQ IDs are now configured locally. SSH verbose diagnostics confirmed the original project key is offered to root at .103 and rejected by the server; the server host key matches the previously trusted .104 identity.
EQ expansion deployment — 19 September 2026
SSH access restored at 10.96.20.103 (same trusted host as previous .104). EMP, Lift and Corridor additions deployed. Added GST (nine EQs, confirmed GST_BATH_SUB and GST_BATH_GRP), SPA (SPA_GRP), and Pool (POOL_MIDHIGH, POOL_SUB, POOL_GRP, POOL_WC). Lift/Corridor remain single entries. All six new area groups contain 29 unique requested IDs, seed once on discovery, and preserve subsequent user edits/deletions. Direct read-only Core inventory found eight EMP EQs, all six-band parametric; EMP_BED_GRP and other newly requested areas were not yet exposed at the time of inspection. No audio writes.
Named EQ areas and navigation — 19 September 2026
EQ sidebar areas are Entertainment, Emperor (EMP), Guest (GST), Lift, Corridor, SPA, and Pool. Click an area to see only its EQs. All EQs is split into labelled collapsible area sections. Lounge, Bedroom, Bathroom and filter names are readable; full component Code Names remain in button tooltips. Known new groups are seeded in live mode even before their components are exposed; unavailable components are disabled. Existing default EMP/GST/001 Entertainment group names migrate to friendly names; custom names and assignments are preserved. Core connection was restored read-only to REM30_DEL_MTRX at 10.96.20.2. No audio values changed.