Skip to content

Creating an interactive countdown timer in Figma

September 21, 2024 | 09:20 PM

Introduction

tl;dr - if you’d like to play with the final result you can find the file on Figma community here. If you’d like to dig into how it works, read on…

This idea stemmed from a recent project where part of the experience involved simulating a fixed time period for group participants to complete an exercise. It made me wonder: could I create a fully functional countdown timer prototype using only Figma variables and component variants? Some of the key things I wanted to achieve:

The 7 segment display

Image of the figma component which provides all the digits 0 to 9

At the most basic level there’s a ‘number’ component which provides all the digits 0 to 9. Finding a good open-source 7-segment font was something of a challenge - and I ended up using DSEG. Using a component with variants for each digit actually makes the challenge harder - but nothing on Google Fonts was quite right.

The timer

Image of the figma component which provides the 'timer' features of the prototype

The timer component has four instances of the number component wrapped up in a single block. Each digit is attached to the output of a variable - Minutes - Tens Minutes - Units Seconds - Tens and Seconds - Units, so as the timer counts down those values are reflected in the display.

Screenshot of timer variables showing 4 variables for minutes and second, as well as 4 variables to return those variables to a known starting point

In Figma variables there’s actually two sets of time variables - Minutes - Tens etc. are the ones that are updated as the timer counts down. The other set of variables are used to reset the timer to a known starting point - so that once you hit stop and start again the timer will count down from the same value. The main time variables are then an alias of the reset values.

Making the timer run

Creating the logic of the timer was actually much trickier that expected (for reasons we’ll come on to) - but the basic concept is quite simple. There’s 4 variants of timer component:

The decrement logic

Screenshot of timer prototype logic explained in the paragraphs following

The logic for decrementing the time looks like this:

You’ll notice that in each step there’s a Changed variable which is reset at the beginning of each cycle. I’d originally hoped that adding a “change to” within each logic step would allow me to bypass the other rules in the set - but Figma will evaluate all actions regardless. Figma also doesn’t currently allow for nested if / else logic, and we want to make sure that the final step (decrement the Seconds - Units) only runs if nothing else has been changed this round - so adding a boolean to store the Changed state is a cheeky hack around that.

The other bit of secret sauce is the Play State variable. When I was trying to introduce the play / pause controls I’d originally planned to switch the variant of the Timer component over to the “Paused” variant to stop the timer, however one of the hazards of using “After delay” interactions in Figma is that once a delay timeout has been set, it’ll still execute those instructions regardless of whether the component variant has been changed in the meantime. Adding a “is the timer still running?” test to each decrement check allows you to wrest control of the timer back from those timeouts when one of the control buttons is pressed.

Wrapping it all up

The pause and stop buttons essentially do the same thing; the only difference being that stop resets the current minutes and seconds values back to the reset values.

The final polish is the finish chime - created by embedding a sound effect into a black screen video tucked into the “Finished” artboard.

Final thoughts

It’s a bit of fun - and it’s exciting to see some of the things you can achieve with a bit of creative deployment of Figma variables.

The perfectionist in me would also like to have achieved this prototype with a single artboard (by binding the state of the timer to a variable and then just modifying that variable to start it playing) however the binding of that play / paused state seems to prevent the “On delay” interactions from firing on the other property… maybe something for another day.