OpenStudio Loading your studio
Skip to content
Docs · Optional

Lua scripting

The script editor, the OpenStudio Lua API, and a first script.

Last updated 16 Sept 2026Development build referenceSource on GitHub

OpenStudio ships a Lua scripting engine with native bindings for tracks, transport, FX chains, sends, automation, audio analysis, freeze, and offline render. This page covers the editor, the namespace, a compact API reference, and examples. Project-editing scripts have the synchronization limits described below.

The script editor

Open View → Script Editor. The window has two panes: the editor on top, where you write Lua, and the console below, where output lands.

  1. Type or paste a script into the editor pane.
  2. Click Run. The script executes against the open project.
  3. Read results and errors in the console pane.
  4. Use openstudio.print(...) anywhere in the script to write your own messages to the console.

Scripts call the native audio engine directly. Track, clip, mixer, and automation edits are not synchronized back into the frontend project state or its undo history, so they may not appear in the UI or survive saving. Use the UI for persistent project edits; keep reusable analysis scripts as files on disk.

The OpenStudio namespace

DAW functions are registered under openstudio.*. Older examples using s13.* do not work in this app revision. Use openstudio.addTrackJSFX(trackId, scriptPath, isInputFX) for a JSFX file and openstudio.getAvailableJSFX() to inspect available effects.

Track functions take a trackId string returned by openstudio.addTrack. Volume is in dB, pan runs from -1.0 (left) to 1.0 (right), and times are in seconds. Master volume is linear, from 0.0 to about 3.98 (+12 dB). Read the argument column in the tables below before assuming a range.

Scope of the API

These signatures were checked against the app scripting bindings at the revision shown above. This compact reference covers common DAW operations; the engine also exposes deferred callbacks and a separate gfx drawing API.

A first script

This read-only script prints the engine’s tempo, time signature, and track count. Paste it into the editor and click Run.

local signature = openstudio.getTimeSignature()
openstudio.print("Tempo: " .. openstudio.getTempo() .. " BPM")
openstudio.print("Time signature: " .. signature.num .. "/" .. signature.den)
openstudio.print("Native tracks: " .. openstudio.getTrackCount())

getTimeSignature() returns a table with num and den fields. A native track count does not provide the IDs of the tracks in the project.

API reference

Signatures below describe the native Lua bindings. Optional arguments have a ? suffix. none means no return value. Track IDs are UUID strings, not numbered positions; keep the IDs returned by addTrack(). FX and send indices are zero-based.

Track operations

FunctionArgumentsReturnsDescription
openstudio.getTrackCount()nonenumberReturns total number of tracks
openstudio.addTrack()nonetrackId: string or nilCreates an audio track; no name argument
openstudio.removeTrack(trackId)trackId: stringbooleanRemoves a track
openstudio.setTrackVolume(trackId, dB)trackId: string, dB: numbernoneSet track volume (-60 to +12 dB)
openstudio.setTrackPan(trackId, pan)trackId: string, pan: numbernoneSet track pan (-1.0 L to +1.0 R)
openstudio.setTrackMute(trackId, muted)trackId: string, muted: booleannoneSet track mute state
openstudio.setTrackSolo(trackId, soloed)trackId: string, soloed: booleannoneSet track solo state
openstudio.setTrackArm(trackId, armed)trackId: string, armed: booleannoneSet track record arm
openstudio.reorderTrack(trackId, newIndex)trackId: string, newIndex: numberbooleanMoves a track to a zero-based position

Transport

FunctionArgumentsReturnsDescription
openstudio.play()nonenoneStart playback
openstudio.stop()nonenoneStop playback
openstudio.record()nonenoneStart recording (arms must be set)
openstudio.isPlaying()nonebooleanCheck if transport is playing
openstudio.isRecording()nonebooleanCheck if transport is recording
openstudio.getPlayhead()nonenumberGet playhead position in seconds
openstudio.setPlayhead(time)time: numbernoneSet playhead position in seconds
openstudio.getTempo()nonenumberGet current BPM
openstudio.setTempo(bpm)bpm: numbernoneSet tempo (20-999 BPM)
openstudio.getTimeSignature()nonetableReturns {num, den}, not two return values
openstudio.setTimeSignature(num, den)num: number, den: numbernoneSet time signature
openstudio.setLoop(enabled)enabled: booleannoneEnables or disables the existing loop range

FX chain

FunctionArgumentsReturnsDescription
openstudio.getTrackFX(trackId)trackId: stringtableGet list of track FX plugins
openstudio.getTrackInputFX(trackId)trackId: stringtableGet list of input FX plugins
openstudio.addTrackFX(trackId, pluginPath)trackId: string, pluginPath: stringbooleanAdds a plugin by its path
openstudio.removeTrackFX(trackId, index)trackId: string, index: numbernoneRemoves the zero-based FX slot
openstudio.bypassTrackFX(trackId, index, bypassed)trackId: string, index: number, bypassed: booleannoneToggle FX bypass
openstudio.addTrackJSFX(trackId, scriptPath, isInputFX?)trackId: string, scriptPath: string, isInputFX: booleanbooleanAdds a JSFX file to the track or input chain
openstudio.getAvailableJSFX()nonetable or nilLists available JSFX effects

Master bus

FunctionArgumentsReturnsDescription
openstudio.setMasterVolume(volume)volume: numbernoneSet master volume (0.0 to about 3.98 linear, +12 dB)
openstudio.getMasterVolume()nonenumberGet master volume
openstudio.setMasterPan(pan)pan: numbernoneSet master pan (-1.0 to +1.0)
openstudio.getMasterPan()nonenumberGet master pan

Sends

FunctionArgumentsReturnsDescription
openstudio.addTrackSend(trackId, destTrackId)trackId, destTrackId: stringnumberAdd send, returns send index
openstudio.removeTrackSend(trackId, index)trackId: string, index: numbernoneRemove send at index
openstudio.setTrackSendLevel(trackId, index, level)trackId: string, index: number, level: numbernoneSet send level (0.0 to 1.0)
openstudio.getTrackSends(trackId)trackId: stringtableGet all sends for a track

Playback clips

FunctionArgumentsReturnsDescription
openstudio.addPlaybackClip(trackId, file, start, duration, offset?, volumeDB?, fadeIn?, fadeOut?)trackId/file: string; times and gain: numbernoneAdds an audio playback clip; optional times/gain default to zero
openstudio.removePlaybackClip(trackId, file)trackId: string, file: stringnoneRemoves playback material by track and path
openstudio.clearPlaybackClips()nonenoneRemove all playback clips

Automation

FunctionArgumentsReturnsDescription
openstudio.setAutomationPoints(trackId, param, pointsJSON)trackId: string, param: string, pointsJSON: stringnonePoints are encoded as a JSON string, not a Lua table
openstudio.setAutomationMode(trackId, param, mode)trackId: string, param: string, mode: stringnoneSet automation mode ("read", "write", "touch", "latch")
openstudio.getAutomationMode(trackId, param)trackId: string, param: stringstringGet automation mode
openstudio.clearAutomation(trackId, param)trackId: string, param: stringnoneClear all automation points

Audio analysis

FunctionArgumentsReturnsDescription
openstudio.measureLUFS(filePath, startTime?, endTime?)filePath: string; times: numbertable or nilFields: integrated, shortTerm, momentary, truePeak, range
openstudio.detectTransients(filePath, sensitivity?, minGapMs?)filePath: string; defaults: 0.5 and 50 mstableReturns detected transient positions
openstudio.reverseAudioFile(filePath)filePath: stringstring or nilReturns the generated output path, or nil on failure
openstudio.detectSilentRegions(filePath, thresholdDb?, minSilenceMs?, minSoundMs?, preAttackMs?, postReleaseMs?)Defaults: -48 dB, 200 ms, 100 ms, 10 ms, 50 mstableDetects silent regions with timing controls in milliseconds

Track freeze

FunctionArgumentsReturnsDescription
openstudio.freezeTrack(trackId)trackId: stringtable or nilReturns the engine freeze result; inspect its fields
openstudio.unfreezeTrack(trackId)trackId: stringbooleanUnfreeze track (restore original)

Render

FunctionArgumentsReturnsDescription
openstudio.renderProject(source, startTime, endTime, filePath, format?, sampleRate?, bitDepth?, numChannels?, normalize?, addTail?, tailMs?)Required source/path: string; start/end: secondsbooleanDefaults: wav, 44100 Hz, 24 bit, stereo, no normalization or tail

MIDI

FunctionArgumentsReturnsDescription
openstudio.getMIDIDevices()nonetableList available MIDI input devices

Metronome

FunctionArgumentsReturnsDescription
openstudio.setMetronomeEnabled(enabled)enabled: booleannoneEnable/disable metronome
openstudio.isMetronomeEnabled()nonebooleanCheck if metronome is enabled

Plugins

FunctionArgumentsReturnsDescription
openstudio.scanForPlugins()nonenoneTrigger VST3 plugin scan
openstudio.getAvailablePlugins()nonetableList all scanned plugins

Utility

FunctionArgumentsReturnsDescription
openstudio.print(...)any valuesnonePrint to script console
openstudio.getAppVersion()nonestringGet OpenStudio version string
openstudio.showMessage(title, message)title, message: stringnoneShow a message dialog
openstudio.fileDialog(...)unavailableraises errorSelect a file before running Lua and supply its explicit path

More examples

Analysis functions take file paths, not track ids, so they work on any audio on disk. This script measures three files and prints integrated loudness and true peak for each. Adjust the paths for your OS.

local files = { "C:/audio/verse.wav", "C:/audio/chorus.wav", "C:/audio/bridge.wav" }
for _, file in ipairs(files) do
    local stats = openstudio.measureLUFS(file)
    openstudio.print(file .. ": " .. stats.integrated .. " LUFS, peak " .. stats.truePeak .. " dBTP")
end

Use returned values to inspect the engine without changing the project. For example, list the plugin names returned by the current scan:

for _, plugin in ipairs(openstudio.getAvailablePlugins() or {}) do
    openstudio.print(plugin.name)
end

For a stereo master render of the first sixty seconds, call openstudio.renderProject("master", 0, 60, "C:/output/mix.wav", "wav", 44100, 24, 2, false, false, 0). This returns a success boolean. Choose an explicit writable output path before running the script.

Tips

  • Start with read-only tasks such as inspecting engine state or batch-measuring audio files.
  • openstudio.print() is the debugging tool. Print ids and return values as you go; inspect returned values before continuing.
  • Native mutation bindings are available, but do not provide the UI’s project synchronization or undo guarantees. Experiment in a disposable session.
  • Save commonly used scripts as files so you can reuse them across projects.
Other extension paths

Lua is one of two supported extension paths; the other is JSFX-style script effects (S13FX) that run inside an FX chain. A native extension SDK is listed under Exploring on the roadmap, conditional on demand for a stable ABI.