Creative Programming

A stopwatch that counts in seconds, minutes and hours can be useful in a variety of projects. In this post, we will go through the steps for creating a stopwatch with only using CHOPs!

The stopwatch will function as a common stopwatch; it will receive a trigger to start counting in seconds, minutes and hours. The stopwatch will also have a pause button and an initialize button. As always, the best way to design our components is to look at them as a black box with inputs and outputs. That way we can reuse the component easily in other projects and keep the modular-way of thinking as one of the first priorities when we are working on projects. The challenge here is to create the stopwatch only using CHOPs. Let's write down our requirements:


  1. Inputs: start button, init button, pause toggle.
  2. Outputs: hour, min, sec as CHOPs, but also as DATs (in the form: 01:45:32)
  3. Use only CHOP operators for the network.

We start by creating a Base COMP which we call base_stopwatch, and we customize the component. We add a custom page called Settings, and our three custom parameters: an Init pulse, a Start pulse, and a Pause toggle.

We can now start our design. Do we have any CHOP operator that can act like a stopwatch? Yes! We have the Timer CHOP. In fact, the Timer CHOP is a stopwatch by itself. The downside is that it only counts in samples, frames or seconds. We need to build a network to be able to count in minutes and hours too. Let's go inside the base COMP and add a Timer CHOP, and three Parameter CHOPs in order to grab the three custom parameters. Then, we reference the custom Init to initialize, the custom Start to start and the custom Pause to play parameters of the Time CHOP. For the Pause channel, we need to connect it to a Math CHOP in order to invert its range (pause and play are opposite operations).

We know that 1 minute is 60 seconds, so every 60 seconds we have a cycle. We can see that the Timer CHOP has a parameter called Cycle. This means that when the Timer CHOP reaches a specific number - a number that we can set via the Length parameter - it will will complete a cycle and start counting again from zero. We turn on the Cycle parameter, and we set the Length of the Timer CHOP to 60 seconds. Also, we don't want the cycles to end (to have a limit), so we turn off the Cycle Limit parameter. We then go to the Outputs page, we set the Time Count to Seconds, the Cycle Pulse to On, and we turn off all other outputs.

What do we have so far? We have a channel that counts the elapsed seconds (timer_seconds) that loops every 60 seconds, and a pulse every time this loop completes. Can we keep track of the number of the pulses? If we can, we can keep track of the elapsed minutes! The perfect CHOP for this function is the Count CHOP. When the Count CHOP receives a trigger in its first input, it starts counting and increments its output by a the value that is specified by its third input. If no third input is connected the increment value is 1, which is exactly what we want. Furthermore, the Count CHOP can loop the counting if we set the Limit parameter to Loop Min/Max, which again is exactly what we want! Of course, we have to set the Limit Minimum to 0, and the Limit Maximum to 59 (not 60, as the Timer CHOP). Let's grab the cycle_pulse channel with a Select CHOP and connect it to a Count CHOP making all the changes described above.

Let's rename the cycle_pulse channel to what it is really now, the min channel.

Well done, we created a channel with the elapsed minutes! Here is the tricky part of the network. The Timer CHOP had the functionality to give us a cycle pulse every time we reached 60 seconds. Unfortunately, the Count CHOP does not have this functionality. We have to create somehow a pulse every time we complete the minute loop. The only thing that we have is the min channel. We can see that the min channel has a transition from 59 to 0 every time the Count CHOP completes its cycle. In other words, we have a transition between two states, a non-zero state to a zero state. That's our valuable information! When it comes to binary states (on/off), one of the first things that is useful to remember is the Logic CHOP. The Logic CHOP can perform boolean (logic) operations, operations with 1s and 0s. First, it converts the channels of all its input CHOPs into binary channels (0 = off, 1 = on), and then combines the channels using a variety of logic operations. The boolean operations that the Logic CHOP contains are AND, OR, XOR, NAND, NOR. The results of these operations are presented below.

Enough with the theory, let's see it in action. We add a Constant CHOP with value of 0 (chan_zero) and we connect the Rename CHOP and the Constant CHOP to a Logic CHOP. By searching in the logic tables above, we can see that with the NOR operation we can output a 1 when we have both of the inputs 0! The chan_zero channel is always 0, and the min channel is 0 only when it completes the minute cycle. As a result, the output of the Logic CHOP will have a transition from 0 to 1 only when a minute cycle is completed. That's how we create our trigger for the hours. We change the Combine CHOPs parameter to Not Or. Then we rename the channel to what it really is now, the hour_trigger (it's not a pulse).

With the same thinking as above, if we connect the hour_trigger into another Count CHOP we can create the hours channel! After the Count CHOP, we add a Rename CHOP to rename the channel to hour.

Let's do some housekeeping by creating some nulls and merge the three channels - sec, min and hour -at the end.

Our last move is to reference the Init parameter to the two Count CHOPs, so every time we initialize the Timer CHOP, we reset the two counters.

As a bonus move, we can convert the CHOP channels to string DATs, and add the string python method .zfill(2), so we always have two digits for all times - hours, minutes, seconds.


In the post we designed a stopwatch, a tool that can have a lot of uses in a variety of our projects. We saw how we can approach a design with an only-CHOPs perspective, and understand the power of this operator family. A stopwatch component is ready to use now. Trigger it, and let it start running!

Written By
Vassilis Malamas