Skip to content

Script API Reference ​

All functions and objects available in the Muxit script sandbox. All calls are synchronous — no await needed (await still works if you prefer).

Drag-and-drop

Every primitive on this page is also draggable. Open the Connectors panel — the Script API section at the top lists delay, ai, say, log.*, emit, on, stream, ask.*, and the others. Drop one into a script editor to insert the call.

Device Access ​

APIDescription
connector(name)Get a device proxy by name
device(name)Alias for connector()
javascript
const psu = connector("psu");
psu.voltage                  // read property (no parens)
psu.voltage = 12             // write property (assignment)
psu.reset()                  // execute action
psu.rampTo(24)               // call custom method

Logging ​

APIDescription
log.info(msg)Info-level log
log.warn(msg)Warning-level log
log.error(msg)Error-level log
log.debug(msg)Debug-level log
console.log(msg)Alias for log.info()

Events ​

APIDescription
emit(event, data)Publish event (namespaced as script:<event>)
on(event, handler)Subscribe to events (auto-cleaned on stop)
stream(connector, stream, data)Emit stream data to dashboards

Timing ​

APIDescription
delay(ms)Sleep (abortable on stop). Always use in loops to avoid excessive CPU usage
timestamp()Current ISO 8601 UTC string

AI ​

APIDescriptionTier
ai(prompt)Single-shot LLM call, returns string responseFree (with local LLM) / Maker (with Muxit-managed)
ai(prompt, image)LLM call with base64 JPEG image — the model sees the picture and answers in textFree (with local LLM) / Maker (with Muxit-managed)
ai(prompt, images[])LLM call with several images for comparison — array of base64 strings or { image, label } objectsFree (with local LLM) / Maker (with Muxit-managed)
say(text, opts?)Send message to Chat Panel; spoken aloud when the speaker toggle in the top status strip is on. opts.emotion hints at delivery style.Free

ai() routes through the active LLM provider configured in Settings → AI Services. When pointed at a local LLM (Ollama, LM Studio, any OpenAI-compatible endpoint) it works on every tier including Free — bring-your-own model, no Muxit credits consumed. When pointed at the Muxit-managed cloud proxy it requires a Maker subscription; Free-tier users get a clean AI_REQUIRES_PRO error (legacy error name — the actual gating tier is Maker). The image overload runs on the same gate as the active provider; what's gated to Pro is the agentic Vision AI tool surface in the Chat Panel and MCP (take_snapshot, identify_objects, OpenCV trackers, spatial mapping) — not the explicit ai(prompt, image) call from a script. say() uses browser text-to-speech and is available on all tiers.

say() details:

say(text, opts?) queues text for the dashboard's text-to-speech engine and shows it as a script bubble in the chat panel. Calls from any script land in a single FIFO queue on the dashboard so they can never overlap or replay as a burst — utterances finish in the order they were emitted.

opts.emotion is a hint string ("excited", "sad", "whisper", "serious", …). The browser TTS engine approximates emotions with rate/pitch tweaks; server-side TTS providers (added in a later release) honour them natively.

js
say("Reading complete.")
say("Calibration done!", { emotion: "excited" })
say("Limit switch triggered.", { emotion: "serious" })

On Free? You have two ways to use ai() without paying for Muxit-managed credits: (1) point Muxit's chat / ai() at a local LLM (Ollama, LM Studio, any OpenAI-compatible endpoint) in Settings → AI Services — it runs on every tier; or (2) use Muxit's MCP server with your own AI client (Claude Desktop, Claude Code, ChatGPT) — that exposes the same connectors and scripts but drives them from outside the Muxit window. See the AI guide.

ai() details:

  • Returns a text string (non-streaming, ~1024 max tokens)
  • Uses the Scripts & tools model configured in Settings → AI Services — set it to point scripts and image recognition at a different (e.g. local, image-optimized) model than the chat assistant. Leave it blank to fall back to the chat model
  • No tool calling — for quick inference only (classify, extract, describe)
  • No conversation history — each call is independent
  • image parameter: base64-encoded JPEG (e.g., from connector('webcam').snapshot)
  • Multiple images: pass an array as the second argument to attach several frames in one call — useful for "what changed?" (before/after) or "is this like A or B?" (reference matching). Items are either bare base64 strings or { image, label?, detail? } objects; a label is sent as a short text block right before its image so the model can refer to it by name. Up to 8 images per call (extras are dropped with a script-log warning).

Where do the images come from? Any source that yields base64 JPEG:

  • Live camera — connector('webcam').snapshot (or the Onvif driver).
  • Reference image from disk — the FileAccess driver: connector('fileaccess').openBinary('refs/good.jpg').read() returns plain base64, ready to pass straight to ai(). Prefer JPEG reference files; PNG/WebP also work (the MIME type is detected automatically).
js
// Text inference
const result = ai("Is 85°C too hot for this motor?");

// Vision analysis (single image)
const snap = connector('webcam').snapshot;
const analysis = ai("Is the part in the correct position?", snap);

// Has anything changed vs the previous snapshot?
const before = connector('webcam').snapshot;
delay(2000);
const after = connector('webcam').snapshot;
const diff = ai("Did anything move or change between these two photos?", [before, after]);

// Is the workpiece like reference A or B? (labelled comparison)
const fa = connector('fileaccess');
const verdict = ai("Does the current part match A (good) or B (rejected)?", [
  { image: fa.openBinary('refs/good.jpg').read(),     label: "A: good" },
  { image: fa.openBinary('refs/rejected.jpg').read(), label: "B: rejected" },
  { image: connector('webcam').snapshot,              label: "Current" },
]);

User Interaction ​

Block the script until a dashboard user answers, or the timeout fires.

APIDescription
ask.confirm(message, opts?)Yes/No prompt — returns true / false
ask.choose(message, choices, opts?)Pick one of the strings in choices
ask.text(message, opts?)Free-form text — returns the user's string

Options (opts):

FieldDescription
timeoutMilliseconds (≥ 1000) to wait before falling back to default / throwing
defaultValue returned if the timeout expires. Omit to throw PROMPT_TIMEOUT
requireObserverWhen true, the timeout clock only ticks while ≥ 1 dashboard is subscribed
  • Return values are indistinguishable from human answers — consult script logs if you need to know whether a default was used.
  • An omitted timeout waits indefinitely (until the script is stopped).
  • timeout < 1000 is rejected at call time as a likely typo (seconds vs milliseconds).
js
// Yes/no with a 30 second timeout, defaulting to "no".
if (!ask.confirm('Home all axes before run?', { timeout: 30000, default: false })) {
    log.warn('User declined — skipping homing');
}

// Force operator attendance: clock pauses if all dashboards disconnect.
const port = ask.choose('Select instrument port', ['COM1','COM2','COM3'], {
    requireObserver: true,
    timeout: 120000,
    default: 'COM1',
});

// No default → throws PROMPT_TIMEOUT if nobody answers.
const label = ask.text('Run label?', { timeout: 60000 });

File I/O ​

The sandbox has no fs, require, or import — for file access, use a FileAccess connector (the default workspace ships one named files, sandboxed to workspace/data/).

Bind the path once with .openText(path) (or .openBinary(path) for base64 I/O) and call methods on the returned handle — the rest of the script doesn't repeat the path:

APIDescription
connector('files').openText(path, delimiter?)Returns a TextFileHandle
connector('files').openBinary(path)Returns a BinaryFileHandle (base64 I/O)
f.write(content)Overwrite. String or array (joined as a CSV row)
f.append(content)Append. String or array. null / undefined is a no-op
f.read() / f.readLines()Read as UTF-8 text or split into lines
f.exists / f.size / f.info()Metadata
f.rename(newPath) / f.delete()Mutations (delete is idempotent; rename updates the handle in place)
connector('files').list(path?)Returns { files, dirs } for a directory
js
const files = connector('files');
const csv = files.openText('temps.csv');
csv.write('time,temp_c\n');
while (script.running) {
  csv.append([timestamp(), connector('sensor').temperature]);  // array → CSV row, auto-quoted
  await delay(1000);
}

Open temps.csv in a tab — the chart/table refreshes automatically each time the script appends a row. Full action list in the FileAccess driver reference.

Script Lifecycle ​

APIDescription
script.runningtrue while active, false after stop
script.nameThis script's filename (without .js)

Pressing Stop interrupts the V8 engine synchronously — statements after the main loop, and finally blocks, are not guaranteed to run. Put setup and safe-state writes before the loop and design each iteration to be safe to abort.

Standard Globals ​

Math, JSON, Date, parseInt, parseFloat, isNaN, isFinite, Number, String, Boolean, Array, Object, Map, Set, Promise

Not Available ​

require, import, fs, process, net, http, child_process, eval, Proxy, Reflect, global, globalThis.

For filesystem access, use a FileAccess connector instead of require('fs').


For detailed explanations and examples, see the Script Guide.

Muxit — Hardware Orchestration Platform