Flocking with SpdeFlocking with Spde

Last month (how time flies) we were talking about how fun it was to write code for the graphical Processing library using Scala inside the Processing Development Environment. Well look, it has gotten even more fun!

This browser does not have a Java Plug-in.
Get the latest Java Plug-in here.

Thanks to a tip in the comments, the applet has been reduced to a disturbing 19,992 bytes with Pack200 compression.

This applet weighs in at 56 KB, smaller than a lot of JavaScript libraries and even some big boring old jpegs. The secret of its slimness is the ProGuard class optimizer which the Scala fork of the Processing environment, Spde, invokes as a final step in exporting an applet.

The code itself is a fairly straight translation of Daniel Shiffman’s Flocking example for Processing. (It has been formatted to fit this screen.) If you haven’t tried clicking, please click now. Every click creates a new bird—it’s a fun little simulation!

Learning Processing

Shiffman recently wrote a book about Processing that seems to be selling rather well—Amazon can’t decide if it’s in stock or out. Learning Processing begins with a very appealing description of itself:

This book tells a story. It’s a story of liberation, of taking the first steps towards understanding the foundations of computing, writing your own code, and creating your own media without the bonds of existing software tools. This story is not reserved for computer scientists and engineers. This story is for you.

Yayyy. That is what this weblog is all about (and also, hello, why Apple’s Cocoa Touch DRM has been a disgusting idea from the beginning). The next section goes on to say that the book may not be for you if you’re already a programmer. (Too late!) It does start off at the very basics of writing software, but that’s fine because some of us are interested in how people teach and learn that stuff anyway.

There’s also a funny section that could only have been prompted by annoying undergraduates demanding why they should learn something that they heard was based on the tragically unhip language of Java. Shiffman delivers a spirited overview of the programming language trendscape, the fact that no language is perfect, that there is no competition for the PDE, and that Processing just plain gets the job done. Finally:

I would suggest that you stop worrying about what it is you should be using and focus on learning the fundamentals with Processing. That knowledge will take you above and beyond this book to any language you want to tackle.

Excellent advice for this year’s class! But maybe not for 2010’s.

The fundamentals of programming

All of us almost certainly learned our programming skills in a similar order: statements, variables, conditionals, loops, then procedures. We learned procedural programming and internalized it to the point that now, when solving a software problem, our minds immediately start declaring variables and acting on them in loops. Sorry guys, but—this is not normal human behavior. Variable declaration and loops are noise that turns most well-balanced people away from programming.

Once you start programming functionally (snobbism alert), you stop turning problems into variables and loops and start getting really beefed at languages like Java that give you no other choice (or the functional option is verbose and sticks out like a bauble). Here is a parallel idea in a completely different technology:

The equivalent of branching and cloning in centralized version control systems, when it exists, requires network communication. … these operations will be slower to some degree, usually to the point where users won’t bother using them unless absolutely necessary. … I experienced these phenomena first-hand. Git was the first version control system I used, and I grew accustomed to it, taking many features for granted. I did not know what centralized systems were like, and assumed they were similar. Later, I was forced to use one…

Just having a feature is not good enough: it has to be convenient. And also, isn’t it trippy to think about someone learning Git first, then struggling with Subversion or whatever, while the rest of us are struggling up a steep learning curve in the opposite direction (uphill both ways) to learn Git? Don’t you wish you had learned Git first?

So here is the theory: if you teach people functional programming first, not only will they learn a better form of programming, but they’ll learn it more readily. Human brains are not composed of registers and logic units. And even though we aren’t all mathletes, the concept of the function was developed by humans, for humans, long before anybody thought about updating variables in instruction loops that repeat thousands of times—an idea that is almost oppressively mechanical in the abstract.

Functional programming is more humane

From the original Flocking example, a method of the Boid class:

// Separation
// Method checks for nearby boids and steers away
Vector3D separate (ArrayList boids) {
  float desiredseparation = 25.0f;
  Vector3D sum = new Vector3D(0,0,0);
  int count = 0;
  // For every boid in the system, check if it's too close
  for (int i = 0 ; i < boids.size(); i++) {
    Boid other = (Boid) boids.get(i);
    float d = loc.distance(loc,other.loc);
    // If the distance is greater than 0 and less than an arbitrary amount
    // (0 when you are yourself)
    if ((d > 0) && (d < desiredseparation)) {
      // Calculate vector pointing away from neighbor
      Vector3D diff = loc.sub(loc,other.loc);
      diff.normalize();
      diff.div(d);        // Weight by distance
      sum.add(diff);
      count++;            // Keep track of how many
    }
  }
  // Average -- divide by how many
  if (count > 0) {
    sum.div((float)count);
  }
  return sum;
}

It’s no coincidence that the method we use to understand procedural code (aside from reading the comments and just pretending to understand it) is to become, temporarily, a very slow and crappy computer. Step through it and make a table of the value of variables in succeeding statements and iterate—gruellingly—multiple times through any loop until it is clear how the machine will interpret the instructions.

Once you see what’s going on in this separate function, it’s possible to translate it into a very opaque, single expression in Scala. But keeping MacIver’s recent post in mind, the one with title crafted to trick the logically lazy, let’s break things into a few smaller blocks:

// is the other bird within a certain distance
def within(dist: Float) = { other: Boid =>
  val d = loc distance other.loc
  d > 0 && d < dist
}

// Sum, and divide by size
def avg(l: List[Vector]) = (l :\ new Vector)(_ + _) / (l.size max 1)

You can work through those if you know the wacky Scala gang signs. If not, they’re fun to learn. Note that Scala allows our version of Vector (defined elsewhere) to use symbols for its arithmetic. Using these blocks, it’s easy to write a separate that, honestly, returns the same results as the one above:

// Separation
// Method checks for nearby boids and steers away
def separate (boids: List[Boid]) = 
  avg(boids.filter(within(25.0)).map { other =>
    (loc - other.loc).normalized / (loc distance other.loc)
  })

Nice? But here’s the kicker. In Flocking there are two other functions that do similar things. In the original, quite reasonably, the loop and conditional structures are entirely repeated for each one. (Check the source if you want to see them.) Why not factor out some logic? Because that’s a pain and probably wouldn’t save any lines of code. Which isn’t meant to be a criticism of the original, at all. It’s good procedural code written for easy student digestion. And let’s face it: if you work in Java, you know you are always writing loops that do similar things as other loops because it’s just easier that way. But what if it were easier to do it the right way?

// Alignment
// For every nearby boid in the system, calculate the average velocity
def align (boids: List[Boid]) = avg(boids.filter(within(50.0)).map(_.vel)) max maxforce

// Cohesion
// For the average location (i.e. center) of all nearby boids, 
// calculate steering vector towards that location
def cohesion (boids: List[Boid]) = boids.filter(within(50.0)) match {
  case Nil => new Vector
  case nearby => steer(avg(nearby.map(_.loc)), false)
}

Gosh, extracting within and avg for the first function sure paid some dividends in the second two! It’s times like these when the excitement surrounding functional programming seems muted, if anything. You might have thought, with graphical programming being all about arrays and loops, that translating some of it into Scala would mean fewer semi-colons and not much else. But then something awesome like this happens.

If you wanted to learn functional programming, say, in your spare time, there can’t be a more fun way to do it than with interactive graphical programming. So just merge this book and that book (Git, a little help?) and off you go. And if someone did write a book on functional graphical programming for absolute beginners, how totally sweet would that be for the next generation of programmers? Maybe they could even be less exclusively male? (Oops, hobbyhorse.)

What the flock

Sorry, this post isn’t going to cover drawing the birds and the program setup business. You’ll have to study the full example sources for that, and to play with this Scala version, you’ll need Spde. The good news is that with the right software it’s easy to run. Um, how large is the intersection of the installed bases of Buildr, Scala, and Git? Anyone out there? Just type this in:

Or you can just download the application package.

git clone git://technically.us/git/spde

Tic tic tic (the repository is now fun sized). Then:

cd spde
buildr run

And Flocking will be under the Examples menu! It is not hard to find because there are only three examples.

Fly fly away, birdies.

Codercomments

Awesome post! Am really excited to see the Flocking example repurposed in this way! And thanks for the mention of the book too.

Going to have to try out this scala fork myself…

Most impressive! The applet starts almost instantly on Java 6uN. Well, not instantly, but a lot faster than any similar flash or Silverlight I’ve ever seen. The only downside is Google Reader seems to choke on it…

If you pack200 the example, it drops from the svelte 56k down to a near anorexic 19. If you’re willing to require Java 5 or greater, the Java plugin will happily use pack200’d code as long as your web server tacks on the right ContentType and Encoding.

AddEncoding pack200-gzip .jpgz
AddType application/x-java-archive .jpgz

does it for Apache.

omg

Processing already requires Java 5 so I’ll do that!

Daniel1: you sure found this post quickly! Please write me if you have any trouble getting Spde running. Or there is a thread I started in Discourse.

Daniel2: Starts faster than Flash? Cooool. I tested on XP and it did seem to run even better there than in Safari or FF Mac.

There’s a Haskell book, “The Haskell School of Expression: Learning Functional Programming through Multimedia”, that practices what you preach:

the author uses examples drawn from multimedia applications, including graphics, animation, and computer music,

But it’s a Haskell book. Processing & Scala is a nicer mix if you ask me and your book combo is a perfect fit.

Awesome cover! It does sound like the same idea, but yeah Processing is where the art and design action is.

Hey, it has killed my ubuntu !

Otherwise, looks good and… thanks as usual for your posts !

++ joseph

I know! As a past and future Ubuntu user (actually right now I’m installing it on a VirtualBox to see this carnage for myself), and also knowing that a decent percentage of this weblog’s readers use Ubuntu, I feel kinda bad. BUT, it is just ridiculous that this bug got through. Come on guys. We finally have applets running great on Windows, well enough on Mac, and then after Sun partners with Ubuntu we have the whole OS going down because of a 20K applet? I’m not going to settle for that. The applet stays!

i was just thinking about my own scala/p5 fork… and you did it, tops dude! =)

Add a comment