Time (clock) based actions

Time (clock) based actions:

for example if the clock gets to 20:00:00 start the Setlist.

Or if the clock reaches 21:59:00 sind midi (to a Drumrack which plays a curfew warning Sound).

This would benefit my production a lot - I could for example automatically start playback 5 minutes before showtime and a countdown would play so the artist knows how much time until the show starts.
As MD it’s always really stressfull to play until the last second of the given slot, so therefore automatically played warnings before curfew would really help.

Hey @Paul_Pichler, welcome to the forum!

What you’re describing is fully doable with a Canvas Script button.
Combine Date (to compute the target wall-clock time), now() (to know how many ms to wait), and await sleep() to schedule the actions in sequence.

Here’s a complete “Arm Show” button that:

  • Starts AbleSet playback at 19:55:00 (so a 5-min countdown track gives way to your first real song at 20:00).
  • Sends a MIDI note to your curfew-warning drumrack at 21:59:00.

Button Type: Script

Script on Press:

// Compute targets (today's date at the given time of day)
const playbackStart = new Date();
playbackStart.setHours(19, 55, 0, 0); // 5 min before showtime

const curfewWarning = new Date();
curfewWarning.setHours(21, 59, 0, 0);

sendOsc("/notify/toast", "all", "Show armed",
  "Playback at 19:55, curfew warning at 21:59", 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 "Your MIDI Output" with the name of the MIDI port routed to your
  // drumrack, and C3 with the note that triggers the warning sound.
  sendMidiNote("Your MIDI Output", 1, "C3", 127, 1000);
  sendOsc("/notify/big", "all", "Curfew in 1 minute", 5000, "amber");
}

The if (msUntil... > 0) guards make sure that if you arm the button after a target time has already passed, that step is skipped (instead of firing immediately, which is what sleep does with a negative value).

I tested this end-to-end with a 15-minute wait — minimized the browser, switched apps, moved between Spaces — and playback fired right on schedule. That said, I recommend tailoring and testing this for your specific use case.

Live countdown label

It’s also nice to see a live countdown to the next event. Add a Label next to the button, switch its Text to Dynamic, and use this template:

${(() => {
  const start = new Date(); start.setHours(19, 55, 0, 0);
  const curfew = new Date(); curfew.setHours(21, 59, 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 switches its message automatically: Show in 02:34:12Curfew in 01:59:00Show ended.

A few caveats

  • The canvas tab/window must stay open until the last scheduled action fires. Canvas scripts run in the browser, so fully closing the tab cancels the pending sleep (you can switch apps or minimize, just don’t close it).
  • The script uses your computer’s system clock, so make sure the time on your machine is correct.

You could also run the same logic from the Project Script (Settings → MIDI Mapping, OSC & Scripting → Project Script). The Project Script runs on the AbleSet server itself, so it’s not tied to a browser tab being open. You could trigger it from the Canvas button via a Shared Variable that the Project Script listens to with onOscChange().

If you’d like to dive deeper into scripting, check out the official tutorial.

Let me know how it goes!

2 Likes

Hey @agustinvolpe

Wow thanks a lot for the quick answer :slight_smile:
I haven’t tried scripting yet, but this looks really cool!

The only thing I was just thinking is if there is a solution for changing Timetables - so a small UI where you could input the times etc would be helpful, so that you don’t have to open/edit the script itself.

I will try some stuff and let you know how it goes!
Thank you :))

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:

  1. Open the canvas, type the two times into the input fields.
  2. Press the button to arm the show once (you’ll see a toast confirming the schedule).
  3. 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!