Skip to content

JavaScript Client Library

The muxit-client library lets you connect any JavaScript application — browser or Node.js — to MuxitServer via WebSocket. It mirrors the script sandbox API, so the same patterns you use in Muxit scripts work from your own code.

Zero dependencies. Single ES module. Drop it in and go.

Installation

Copy lib/muxit-client/muxit-client.js from the Muxit repository into your project.

javascript
// Browser
import { Muxit } from './muxit-client.js'

// Node.js 22+
import { Muxit } from './muxit-client.js'

// Node.js < 22
import WebSocket from 'ws'        // npm install ws
globalThis.WebSocket = WebSocket
import { Muxit } from './muxit-client.js'

Connect

javascript
import { Muxit } from './muxit-client.js'

const muxit = new Muxit()
await muxit.connect('ws://127.0.0.1:8765/ws')

For remote connections, pass an auth token:

javascript
await muxit.connect('ws://server:8765/ws', { token: 'your-auth-token' })

The library auto-reconnects on disconnect with exponential backoff. Disable with { reconnect: false }.

Control Devices

The connector() method returns a proxy that works exactly like the script sandbox — every property access returns an async function:

javascript
const psu = muxit.connector('psu')

// Read a property
const voltage = await psu.voltage()

// Set a property
await psu.voltage(12)

// Call an action
await psu.output(true)

// Multi-arg actions
const robot = muxit.connector('robot')
await robot.moveAbsolute([100, 200, 50])

Reactive State Updates

MuxitServer pushes device state changes every 50ms. Subscribe with events:

javascript
// All connectors
muxit.on('state', (updates) => {
  // { "psu": { "voltage": 12.1 }, "robot": { "position": [100, 200, 50] } }
})

// Single connector
muxit.on('state:psu', (props) => {
  document.getElementById('voltage').textContent = props.voltage
})

Image Streams

Subscribe to camera feeds and other data streams. Image data arrives as StreamFrame objects that are ready for Canvas 2D or WebGL:

javascript
muxit.subscribeStream('camera', 'feed')

muxit.on('stream:camera:feed', async (frame) => {
  // Option 1: Display in an <img> tag
  document.getElementById('preview').src = frame.dataUrl

  // Option 2: Draw on Canvas 2D
  const bmp = await frame.imageBitmap()
  ctx.drawImage(bmp, 0, 0)

  // Option 3: Use as WebGL texture
  const bmp = await frame.imageBitmap()
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bmp)

  // Option 4: Raw bytes for custom processing
  const buf = await frame.arrayBuffer()
})

// Unsubscribe when done
muxit.unsubscribeStream('camera', 'feed')

StreamFrame Properties

Property / MethodReturnsEnvironment
frame.base64stringUniversal
frame.dataUrlstringUniversal
frame.arrayBuffer()Promise<ArrayBuffer>Universal
frame.blob()Promise<Blob>Browser only
frame.imageBitmap()Promise<ImageBitmap>Browser only

AI Integration

Call the AI assistant with a one-shot prompt:

javascript
const answer = await muxit.ai('What is the current voltage on the PSU?')
console.log(answer)

// With an image
const description = await muxit.ai('Describe what the camera sees', base64Image)

Script Management

Start and stop server-side scripts:

javascript
await muxit.scripts.start('monitor.js')

const running = await muxit.scripts.list()
console.log(running)

await muxit.scripts.stop('monitor.js')

Server Info

Query the server for available connectors, schemas, and drivers:

javascript
const names = await muxit.connectors()       // ['psu', 'robot', 'camera']
const schema = await muxit.schema('psu')     // properties, actions, streams
const drivers = await muxit.drivers()         // all available drivers

Events

The library emits events for all server broadcasts:

javascript
muxit.on('connect', () => console.log('Connected'))
muxit.on('disconnect', ({ code, reason }) => console.log('Lost connection'))
muxit.on('server:log', (entry) => console.log('[server]', entry.message))
muxit.on('script:say', (msg) => console.log('[script]', msg.text))
muxit.on('error', (err) => console.error(err.code, err.message))

Every on() call returns an unsubscribe function:

javascript
const unsub = muxit.on('state:psu', handler)
// Later...
unsub()

Logging

Local logging helpers that emit 'log' events:

javascript
muxit.on('log', ({ level, message, timestamp }) => {
  console.log(`[${level}] ${message}`)
})

muxit.log.info('System ready')
muxit.log.warn('Voltage dropping')
muxit.log.error('Connection lost to instrument')

Utilities

javascript
await muxit.delay(2000)         // sleep 2 seconds
console.log(muxit.timestamp())  // "2024-01-15T10:30:00.000Z"

Low-Level Access

For message types not covered by the high-level API, use request() directly:

javascript
// Any WebSocket message type
const config = await muxit.request({ type: 'config.get' })
const logs = await muxit.request({ type: 'server.logs.history' })

// Fire-and-forget
muxit.send({ type: 'some.message', data: 'value' })

See the WebSocket API Reference for all available message types.

Full Example: Live Dashboard

Here's a complete example that reads a power supply, displays a camera feed, and updates a dashboard:

html
<!DOCTYPE html>
<html>
<head>
  <title>Muxit Dashboard</title>
  <style>
    body { font-family: sans-serif; background: #1a1a1a; color: #eee; }
    #stats { display: flex; gap: 2rem; margin: 1rem; }
    .stat { font-size: 2rem; }
    .label { font-size: 0.8rem; color: #888; }
    canvas { border: 1px solid #333; margin: 1rem; }
  </style>
</head>
<body>
  <div id="stats">
    <div>
      <div class="label">Voltage</div>
      <div class="stat" id="voltage">--</div>
    </div>
    <div>
      <div class="label">Current</div>
      <div class="stat" id="current">--</div>
    </div>
  </div>
  <canvas id="camera" width="640" height="480"></canvas>

  <script type="module">
    import { Muxit } from './muxit-client.js'

    const muxit = new Muxit()
    await muxit.connect('ws://127.0.0.1:8765/ws')

    // Update stats from reactive state
    muxit.on('state:psu', (props) => {
      if (props.voltage !== undefined)
        document.getElementById('voltage').textContent = props.voltage.toFixed(2) + ' V'
      if (props.current !== undefined)
        document.getElementById('current').textContent = props.current.toFixed(3) + ' A'
    })

    // Draw camera feed on canvas
    const ctx = document.getElementById('camera').getContext('2d')
    muxit.subscribeStream('camera', 'feed')
    muxit.on('stream:camera:feed', async (frame) => {
      const bmp = await frame.imageBitmap()
      ctx.drawImage(bmp, 0, 0, 640, 480)
      bmp.close()
    })

    // Set PSU to 5V on startup
    const psu = muxit.connector('psu')
    await psu.voltage(5)
    await psu.output(true)
  </script>
</body>
</html>

Comparison with Script Sandbox

If you've used Muxit scripts, the client library will feel familiar:

Script Sandboxmuxit-clientNotes
connector('psu').voltage()muxit.connector('psu').voltage()Same proxy pattern
log.info(msg)muxit.log.info(msg)Local events only
emit(event, data)muxit.emit(event, data)Client-side only
on(event, fn)muxit.on(event, fn)Receives server broadcasts
delay(ms)muxit.delay(ms)Same
timestamp()muxit.timestamp()Same
ai(prompt, img)muxit.ai(prompt, img)Same
say(text)muxit.say(text)Same
stream(c, s, d)muxit.subscribeStream(c, s)Subscribe to receive, not emit
script.runningmuxit.connectedConnection state

The main difference: scripts run inside the server with direct driver access, while the client library communicates over WebSocket. Both use the same underlying protocol.

Muxit — Hardware Orchestration Platform