Chain Chain Chain

I have always been interested in tiny code–the clever solution that seems too small to work.

In the 1970s, Atari game programmers crammed games into 1 or 2k cartridges. They used tricks that would never occur to us today, like scanning through the binary code data to see if any of the code could be repurposed (perhaps with a few tweaks) to be used as graphics. For the Atari Chess game, they went scanning through the logic code to try to see if any strings of bytes looked like a rook, or a bishop.

Another trick was to write your machine language code so that it did one thing if you executed the code starting at one byte, and another thing if you jumped in at the next byte. (Remember the processor was the 6502. Instructions were either one or two bytes long. Writing code interleaved in such a way was a puzzle, but could pay off in a big way if it meant the difference between a 1k ROM and a 2k ROM game cartridge.)

As computers got more powerful, the things you could do in 1k of code got more impressive. The 1k competitions for multimedia computers like the Amiga and the Atari ST were amazing, sometimes featuring 3D objects whirling about over starfields.

(We game programmers always wondered if the guys who won these competitions could write a decent videogame. That’s how we judged whether you were a good programmer or not. I don’t think we ever found out.)

But Seriously…

Aside from the “demo scene,” there have always been more serious programmers who admired pithy one-liners. Anyone who really digs APL or Perl is a candidate.

Yeah, Mine’s Big

We rarely think about code size any more. Our code is dwarfed by the multimedia assets we pull in. Does it matter how big your JavaScript is? It does matter, because the JavaScript code is being served over and over and over, but mostly you want it to work right, and you can get so wrapped up in the nightmares of compatibility that it’s easy to ignore your code size. “I’ll just gzip it later” is the universal thought.

But, thankfully, someone still cares about code size.

This story at Ajaxian caught my interest. It’s about an entry (by coder Mathieu ‘P01′ HENRI) in a 20-line JavaScript contest, which comes from this thread over at OzoneAsylum.

If you have a modern browser that supports Canvas (Firefox, Safari, or Opera), this short program dazzles you with a growing, rotating, glowing pattern. Here is the official link to the entry…

http://www.p01.org/releases/DHTML_contests/files/20lines_hypno_trip_down_the_fractal_rug/

Chain Chain Chain

The effect is a great one, but what really got me excited was the source code. Especially this bit…

//chain a bunch of CanvasRenderingContext2D methods
for( chainThat in {set:1,switchTo:1,clearRect:1,save:1,
     translate:1,rotate:1,drawImage:1,scale:1,
     restore:1,fillRect:1,moveTo:1,lineTo:1,
     beginPath:1,closePath:1,stroke:1,fill:1,arc:1} )
{
  window.fx.CanvasRenderingContext2D[chainThat] =
    chain(window.fx.
      CanvasRenderingContext2D[chainThat]);
}

And here’s the code for the “chain” method…

//  chain( func )
//  make func chainable by making it return
//    its ReturnValue || this
  function chain( func )
  {
    return function()
    {
      return func.apply(this,arguments)||this;
    }
  }

This clever bit of metaprogramming turns a bunch of Canvas functions into functions that can be chained (also known as a cascaded). I don’t know if p01 came up with this trick, or whether he stumbled upon it somewhere else, but it’s a great idea. It lets him perform a whole series of operations in one line.

/* 15 */ fx
.logicCtx
	.set( 'globalCompositeOperation', 'source-over' )
	.clearRect( 0, 0, 256, 192 )
	.save()
	.translate( 96, 96 )
	.rotate( (now/5841%2)*Math.PI )
	.scale( 1+2*((now/1274)%1), 1+2*((now/1274)%1) )
	.drawImage( fx.fooCtx.canvas,0,0,192,192, -288,-288,576,576 )
	.drawImage( fx.fooCtx.canvas,0,0,192,192, -96 ,-96 ,192,192 )
	.drawImage( fx.fooCtx.canvas,0,0,192,192, -32 ,-32 ,64 ,64  )
	.restore()
	.set( 'globalCompositeOperation', 'copy' )
//	prepare for hypnoglow
.switchTo( fx.renderCtx )
	.set( 'globalCompositeOperation', 'source-over' )
	.clearRect( 0, 0, 192, 192 )
	.drawImage( fx.logicCtx.canvas, 0, 0 )
	.set( 'globalCompositeOperation', 'lighter' );

See the dots (the periods) that chain the methods together?

That whole bit counts as one line of JavaScript (line 15, as you can see by the comment), for the sake of this contest (it would be uglier on one physical line, but it’d work).

Users of the jQuery library are used to this chaining. It works if you return this instead of letting the default value undefined be returned.

Next time I use Canvas, I’m stealing this trick.

True story.

When I took Numerical Methods in college, our APL programs were graded based on the number of characters in the program. This was hardcore APL, on a timeshare DEC computer with dedicated APL Greek-letter keyboards and special video terminals that could display the Greek APL symbols (this was back in the days of monochrome text displays–before graphics terminals were common).

About these ads

3 Comments

  1. June 30, 2008 at 5:51 pm

    :D thanks!

    FWIW I came up with and implemented the chaining by myself but having coded in C++ and seen things like jQuery, I knew that kind of syntax. Actually I always loved chainning things in C++. But I never saw anyone chain Canvas methods before.

    [shameless_plug] Have you looked at my 256b ( and smaller ) prods in JavaScript and Assembler ? [/shameless_plug]

  2. rhettanderson said,

    June 30, 2008 at 6:05 pm

    I’ll be sure to look at your stuff. The demo scene has always been an attraction for me.

  3. August 9, 2010 at 2:47 am

    [...] isn’t something ground-breaking, other people done it before (Canto.js, MDC canvas, p01, …) but I decided to implement it by myself since it’s something simple and the other [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: