whitney balls

clean-up #4:

this is something i originally wrote for the article Audiovisuals with SC and now rewrote as separate classes. there are three classes all implementing the same system slightly differently. one for graphics (Whitney) and the other two for patterns (Pwhitney, Pwhitney2).
the principle for this system is described by John Whitney in his book Digital Harmony (1980) and reimplemented musically by Jim Bumgardner in his Whitney Music Box (2006). the idea is simple but complex patterns arise - both in graphics and in harmony/rhythm. the innermost ball moves at a certain speed, the ball next to it doubles that speed and the ball next to that triples the speed etc. there's a sound played for each ball as it passes through 0. each ball's sound plays at a different frequency mapped to some scale, overtone series or something else.

update 131118: added Whitney2 class and new help docs - just download the attached zip file again

the code for the above video example is taken from the helpfile...

(
s.latency= 0.05;
s.waitForBoot{
        var width= 500, height= 500;
        var w= Window("Whitney balls", Rect(99, 99, width, height), false);
        var u= UserView(w, Rect(0, 0, width, height));
       
        SynthDef(\sin, {|freq= 400, amp= 0.2, pan= 0, rel= 0.9|
                var e= EnvGen.ar(Env.perc(0.005, rel), 1, doneAction:2);
                var z= SinOsc.ar(freq, e*pi, e);
                Out.ar(0, Pan2.ar(z, pan, amp));
        }).send(s);
        s.sync;
       
        //--set up the whitney system
        f= Whitney(20, {|ball, i|
                Synth(\sin, [\freq, (64+i).midicps, \amp, 2/f.num]);
                Pen.fillOval(Rect.aboutPoint(Point(ball.x, ball.y), 5, 5));
        });
       
        u.drawFunc= {
                Pen.rotate(2pi*0.75, width*0.5, height*0.5);
                Pen.translate(width*0.5, height*0.5);
                Pen.strokeColor= Color.red;
                Pen.line(Point(0, 0), Point(0, width*0.5));
                Pen.stroke;
                Pen.color= Color.grey(1, 0.7);
               
                f.next;                         //update the system
                f.balls.do{|b, i|               //draw the balls
                        Pen.strokeOval(Rect.aboutPoint(Point(b.x, b.y), 4, 4));
                };
        };
       
        CmdPeriod.doOnce({if(w.isClosed.not, {w.close})});
        u.background= Color.black;
        w.front;
       
        u.animate= true;        //replace this line with the one below if you use swingosc
        //Routine({while({w.isClosed.not}, {u.refresh; (1/60).wait})}).play(AppClock);
};
)

f.speed= 0.02                   //change speed
f.speed= 0.002
f.theta= 0                      //restart
f.spread= 15                    //change distance between balls
f.spread= 5
(
f.func= {|ball, i|
        Synth(\sin, [
                \freq, i.linexp(0, f.num-1, 300, 3000),
                \amp, i.linlin(0, f.num-1, 0.1, 0.05),
                \rel, i.linlin(0, f.num-1, 1, 0.2),
                \pan, 0.4.rand2
        ]);
        Pen.fillOval(Rect.aboutPoint(Point(ball.x, ball.y), 5, 5));
}
)
f.num= 10
f.num= 30
f.num= 55
f.speed= 0.01
f.speed= 0.0002
f.speed= 0.001
f.speed= 0.005

(
f.func= {|ball, i|
        Synth(\sin, [
                \freq, (i.degreeToKey(Scale.indian)+48).midicps,
                \amp, i.linlin(0, f.num-1, 0.1, 0.05),
                \rel, i.linlin(0, f.num-1, 2, 0.2),
                \pan, 0.4.rand2
        ]);
        Pen.fillOval(Rect.aboutPoint(Point(ball.x, ball.y), 6, 6));
};
f.num= 36;
f.theta= 0;
f.spread= 6;
f.speed= 0.001;
)

AttachmentSize
Package icon Whitney.zip50.29 KB