Add a timer to time the actual set length

Having the song and remaining set length timestamps is so helpful. To push that even further, what if there were an option to time your actual set length each night?

As in - start a timer at the song and/or section of your choice, and end the timer when a certain song and/or section ends. A big reason I time the show each night is to have info if/when we do a festival/private event with a reduced set length.

I’d also make use of setting multiple timers in a show (i.e. timing out different acts within a show in real-time)

Hey @kylecmoore,

You can already build this with Canvas (available in Pro) using Shared Variables. Shared Variables are synced across all devices, so the same timer shows up on every remote at once.

The timer is stored as two variables: timer1Elapsed (time accumulated so far, in milliseconds) and timer1Start (the timestamp of the current running segment, empty when paused). That way Start and Stop act as a real pause/resume — only Reset takes it back to zero.


1. The timer display (Label)

Add a Label element and set its value to:

${formatDuration((shared("timer1Elapsed", 0) + (shared("timer1Start") ? now() - shared("timer1Start") : 0)) / 1000)}

While the timer is running it adds the current segment to whatever has already been accumulated; when it’s paused it just shows the accumulated total. now() updates every second inside a template, so the Label counts up on its own.

Optionally, switch the Label’s Background Color to Dynamic so it lights up while running:

${shared("timer1Start") ? "green-700" : "gray-800"}

2. Start / Stop / Reset buttons

Add three Buttons with Button Type set to Script.

Start (or Resume)Script on Press:

// Only starts a new segment if the timer isn't already running
if (!shared("timer1Start")) {
  setShared("timer1Start", now());
}

Stop (or Pause)Script on Press:

// Banks the current segment into the accumulated total, then stops
if (shared("timer1Start")) {
  setShared(
    "timer1Elapsed",
    shared("timer1Elapsed", 0) + (now() - shared("timer1Start"))
  );
  resetShared("timer1Start");
}

ResetScript on Press:

resetShared("timer1Start");
resetShared("timer1Elapsed");

That alone gives you a working stopwatch you can control from any device — and since Stop banks the time instead of discarding it, you can pause during a break and pick right back up where you left off.


3. Starting and stopping automatically at a given song or section

If you’d rather have it fire on its own, you could try driving the same variables from the Project Script (Settings → MIDI Mapping, OSC & Scripting → Project Script). It runs whenever AbleSet or the project loads, and onOscChange() lets you react to the active song changing:

// One entry per timer. Names must match your song locators exactly.
const TIMERS = [
  { key: "timer1", startSong: "Opener", endSong: "Act One Closer" },
  { key: "timer2", startSong: "Act Two Opener", endSong: "Encore" },
];

let prevSong = null;

onOscChange("/setlist/activeSongName", ([song]) => {
  for (const t of TIMERS) {
    // Start when the start song becomes active (and hasn't run yet tonight)
    if (
      song === t.startSong &&
      !shared(`${t.key}Start`) &&
      !shared(`${t.key}Elapsed`)
    ) {
      setShared(`${t.key}Start`, now());
    }

    // Stop when we leave the end song
    if (
      prevSong === t.endSong &&
      song !== t.endSong &&
      shared(`${t.key}Start`)
    ) {
      setShared(
        `${t.key}Elapsed`,
        shared(`${t.key}Elapsed`, 0) + (now() - shared(`${t.key}Start`))
      );
      resetShared(`${t.key}Start`);
    }
  }

  prevSong = song;
}, true);

To trigger on sections instead of songs, swap /setlist/activeSongName for /setlist/activeSectionName and use your section names.

One thing worth knowing: there’s no “song ended” event, so the stop fires the moment the active song changes away from your end song. That works perfectly for timing an act in the middle of a show, but if your end song is the very last one in the set, you’ll want to hit the manual Stop button for that one.


4. Multiple timers

Each timer is just a pair of Shared Variables, so you can run as many as you like side by side — timer1Elapsed/timer1Start, timer2Elapsed/timer2Start, and so on. Add one Label per timer (swapping the key in the template), one set of buttons if you want manual control over it, and a matching entry in the TIMERS array above.

You could also run a “Total Show” timer alongside the per-act ones — same setup, just starting on your first song and ending on your last.

And since it’s all Shared Variables, don’t forget to hit Reset before the next night, or the new show will keep counting on top of the previous one.

Would that work for your use case?

Oh that’s amazing, I hadn’t realized that! Thanks for laying all of this
out.

Re: In concept, that would work. I’ll give this a go asap and report back.

1 Like