Hey @Paul_Pichler,
Yes, you can absolutely build a small UI for inputting times. You could achieve this with Input Field elements with a Linked Variable: whatever you type gets stored in a local variable that the script and the countdown label both read with local().
Here’s the layout. Drop these three elements on your canvas:
1. Input Field — Show Start Time
- Type: Text
- Linked Variable:
playbackTime
- Value:
19:55 (default shown the first time)
- Script on Change:
setLocal("playbackTime", value, true);
2. Input Field — Curfew Warning Time
- Type: Text
- Linked Variable:
curfewTime
- Value:
21:59
- Script on Change:
setLocal("curfewTime", value, true);
The setLocal(..., true) with persist=true is what makes your values survive a reload. The Linked Variable handles the live sync; persistence has to be explicit.
3. For the button element:
Label field — Live countdown
${(() => {
const [ph, pm] = local("playbackTime", "19:55").split(":").map(Number);
const [ch, cm] = local("curfewTime", "21:59").split(":").map(Number);
const start = new Date(); start.setHours(ph, pm, 0, 0);
const curfew = new Date(); curfew.setHours(ch, cm, 0, 0);
const t = now();
if (t < start.getTime()) return `Show in ${formatDuration((start.getTime() - t) / 1000)}`;
if (t < curfew.getTime()) return `Curfew in ${formatDuration((curfew.getTime() - t) / 1000)}`;
return "Show ended";
})()}
The label updates every second and automatically switches from Show in HH:MM:SS to Curfew in HH:MM:SS to Show ended as time passes.
- Button Type: Script
- Script on Press:
const playbackTimeStr = local("playbackTime", "19:55");
const curfewTimeStr = local("curfewTime", "21:59");
const [ph, pm] = playbackTimeStr.split(":").map(Number);
const [ch, cm] = curfewTimeStr.split(":").map(Number);
// Abort with a red banner if either input isn't HH:MM
if ([ph, pm, ch, cm].some(isNaN)) {
sendOsc("/notify/big", "all", "Invalid time format. Use HH:MM", 3000, "red");
return;
}
const playbackStart = new Date();
playbackStart.setHours(ph, pm, 0, 0);
const curfewWarning = new Date();
curfewWarning.setHours(ch, cm, 0, 0);
sendOsc("/notify/toast", "all", "Show armed",
`Playback at ${playbackTimeStr}, curfew warning at ${curfewTimeStr}`, 3000);
// 1. Wait until playback start, then play
const msUntilPlay = playbackStart.getTime() - now();
if (msUntilPlay > 0) {
await sleep(msUntilPlay);
sendOsc("/global/play");
sendOsc("/notify/toast", "all", "Showtime!", "Playback started", 3000);
}
// 2. Wait until curfew warning, then send MIDI
const msUntilCurfew = curfewWarning.getTime() - now();
if (msUntilCurfew > 0) {
await sleep(msUntilCurfew);
// Replace with your MIDI output name + the note that triggers your warning sound
sendMidiNote("Your MIDI Output", 1, "C3", 127, 1000);
sendOsc("/notify/big", "all", "Curfew in 1 minute", 5000, "amber");
}
How the workflow would go:
- Open the canvas, type the two times into the input fields.
- Press the button to arm the show once (you’ll see a toast confirming the schedule).
- At each target time, the corresponding action fires automatically.
The countdown label gives you a live view of how long until the next event, and the input fields remember your values between reloads thanks to the setLocal(..., true) call.
Give it a try and let me know how it goes!