[chuck-users] other ways to live-code with time

Tom Lieber lieber at princeton.edu
Tue Apr 28 21:36:56 EDT 2009

impromptu is a live-coding environment for OS X that looks very
interesting, definitely something I need to try out once I've escaped
the clutches of my end-of-year work.


I thought I'd bring it up not only because it's interesting, but
because its tutorial on "time" contrasts impromptu's asynchronous
model with ChucK's synchronous one: in impromptu, time passes while
your code is running.

You can read the tutorial here:


Just think... you could load a WAV file without hanging the VM! It's
interesting to see what you get when you trade one complication
(waiting on slow shreds) for another (having "now" change beneath your
feet). Here are some, from the tutorial:

"There are a couple of gotcha's to keep in mind when doing schedule
and forget programming. The first thing to keep in mind is that (now)
is an airy fairy thing. In the example below the two notes may be
scheduled to play on the same sample - but then again, they may not -
(now) may have moved forward in time between the two calls (even if
evaluated at the same time).

(play-note (now) piano 60 80 *second*)
(play-note (now) piano 72 80 *second*)

"Often this lack of precision is fine (i.e. too small a change to be
noticeable) but where absolute accuracy is required a time variable
should be used.

(let ((time (now)))
   (play-note time piano 60 80 *second*)
   (play-note time piano 72 80 *second*))

"This inaccuracy becomes more of an issue when amplified over time
such as using (now) inside a recursive callback loop. We can avoid the
problem by precisely incrementing a time value between each recursive
callback (note that any arguments required by the function being
called back must also be passed to callback).

;This is bad
(define loop
  (lambda ()
    (play-note (now) piano 60 80 *second*)
    (callback (+ (now) *second*) 'loop)))


;This is good (precise time arg is now incremented each recursion)
(define loop
  (lambda (time)
    (play-note time piano 60 80 *second* )
    (callback (+ time *second*) 'loop (+ time *second*))))

(loop (now))

"The second major gotcha in recursive callback loops is that (now) is
now. Code requires some time to execute. If you are executing a call
to evaluate a note (now) by the time the code is evaluated it will
already be late. You should always try to schedule your code execution
ahead of the scheduled time of your tasks.

;This is best (callback happens 4100 samples earlier than new time)
(define loop
  (lambda (time)
    (play-note time piano 60 80 1.0)
    (callback (+ time 40000) 'loop (+ time 44100))))

(loop (now))

"In the this-is-good version of loop the time sent as an argument to
loop is exactly the same time as the scheduled callback time. The
problem with this is that the next note needs to be scheduled at
exactly the same time that the function is called. The note will
always be late. The this-is-best version schedules the callback just
ahead of the time that we want to schedule the note. This gives us
4100 samples to execute the code to schedule the note before the note
is required to sound."

The last sentence reminds me of the thread a while back about
benchmarking UGens. :)

Tom Lieber

More information about the chuck-users mailing list