node knockout heatwave [ 2011-09-01 07:42:40 UTC ]

This weekend I took part in node knockout along with Peteris Krumins, David Wee, and Josh Holbrook on team replicants.

Our entry, heatwave, uses node-heatmap to draw a heatmap over your code in realtime to show the parts that are most active as the code executes.

Heatwave accomplishes this trick using bunker to add hooks around every expression. When these hooks get called, the heatmap gets updated!

Check out the live demo and if you like what you see, vote for us!


heatmaps for node.js and the browser [ 2011-07-29 16:12:58 UTC ]

Introducing node-heatmap, a nifty node.js module for making pretty heatmaps. You can even use node-heatmap in the browser with browserify.

Here's an example of using heatmap in node with some random values biased towards the center of the canvas:

var heatmap = require('heatmap'); var heat = heatmap(500, 500, { radius : 30 }); for (var i = 0; i < 5000; i++) { var rho = Math.random() * 2 * Math.PI; var z = Math.pow(Math.random(), 2) * 200; var x = 250 + Math.cos(rho) * z; var y = 250 + Math.sin(rho) * z; heat.addPoint(x, y); } heat.draw(); var fs = require('fs'); fs.writeFileSync('blob.png', heat.canvas.toBuffer());

When the example calls .addPoint(x,y), heatmap draws a rectangle with a radial gradient and outwardly diminishing alpha mask so that the alpha values can pile up as nearby points are added. With Canvas this is surprisingly easy to do:

Heat.prototype.addPoint = function (x, y, radius) { var ctx = this.alphaCanvas.getContext('2d'); var g = ctx.createRadialGradient(x, y, 0, x, y, radius); var a = 1 / 10; g.addColorStop(0, 'rgba(255,255,255,' + a + ')'); g.addColorStop(1, 'rgba(255,255,255,0)'); ctx.fillStyle = g; ctx.fillRect(x - radius, y - radius, radius * 2, radius * 2); };

I got the idea to use a secondary canvas's alpha channel with radial gradients from heatmap.js, which is yet another heatmap module which may be of interest.

Later when it comes time to .draw() onto the primary canvas, I just use color-convert to convert an HSL coordinate to rgb. HSL is super neat because the hue is just an angle, so a loop of i += 30 from 0 to 360 degrees hits all the rainbow colors along the way. All .draw() needs to do is map the composite alpha value as the HSL hue at every pixel onto the RGB of the primary canvas. Easy!

And here's what the final product from the example code at the start looks like:

Radical. Check out the code on github and npm install heatmap!

Mr. Color approves of this module.


javascript AST trickery with burrito [ 2011-07-07 05:58:29 UTC ]

Uglify is a pretty nifty javascript minifier but it also has the lesser-known ability to generate and walk the AST.

Unfortunately, uglify's traverser is a bit cumbersome to use so I wrote a library using my own traverser to make it easy!

Introducing burrito, a crazy new library to make AST traversals and manipulations crazy easy.

Given this snippet of code:

f() && g(h()); foo();

It's super easy to wrap all the function calls in another function call, qqq():

var burrito = require('burrito'); var src = burrito('f() && g(h())\nfoo()', function (node) { if (node.name === 'call') node.wrap('qqq(%s)'); }); console.log(src);

$ node wrap.js qqq(f()) && qqq(g(qqq(h()))); qqq(foo());

Or maybe dive in and surgically replace that && with a ||, how about!

var burrito = require('burrito'); var src = burrito('f() && g(h())\nfoo()', function (node) { if (node.name === 'binary') node.wrap('%a || %b'); }); console.log(src);

$ node wrap.js f() || g(h()); foo();

Plus you can .microwave() your burrito too, which evals the code using node's vm.runInNewContext() (which even runs in the browser thanks to browserify).

var burrito = require('burrito'); var res = burrito.microwave('Math.sin(2)', function (node) { if (node.name === 'num') node.wrap('Math.PI / %s'); }); console.log(res);

This snippet takes the expression Math.sin(2) and replaces the 2 with Math.PI / 2 thanks to some %s interpolation. And since Math.sin(Math.PI / 2) evaluates at 1, the code should print as much...

$ node microwave.js 1

And it does! Hooray!

This craziness makes an amazing number of things possible, like stack traces that work in all the browsers without relying on a stack trace API or code coverage tools without any binary extensions!

Give burrito a spin!

npm install burrito

or get the code on github!


singsong at music hack day [ 2011-05-11 18:02:13 UTC ]

Just off the plane from the first ever nodeconf where I gave a talk about dnode culminating in a live dnode-powered markov rap battle, I headed down to Music Hack Day, San Francisco with Marak to represent node.js.

Music Hack Day is a 24 hour hacking contest for musical hacks. I got in late but stayed the whole night and managed to hack up singsong, interface on top of festival's little-known singing voice synthesizer plugin. You can click on the staff to plop notes down and then use the text boxes below to associate bits of text with them.

Sample output: [ ogg ] | [ wav ]

It's not quite ideal since you've got to break up the words by syllables yourself. I wrote a lib that can do this automatically but didn't hack it in. Good enough for 24 hours at least. Check it out on github.


browserify: browser-side require() for your node.js [ 2011-03-29 00:03:52 UTC ]

Announcing browserify, a node.js module that bundles up all your javascript into a single file so you can use node.js-style require() from the browser.

Check this:

server.js

var connect = require('connect'); var server = connect.createServer(); server.use(connect.static(__dirname)); server.use(require('browserify')(__dirname + '/js')); server.listen(9797);

js/foo.js

var bar = require('./bar'); module.exports = function (x) { return x * bar.coeff(x); };

js/bar.js

exports.coeff = function (x) { return Math.log(x) / Math.log(2) + 1; };

index.html

<html> <head> <script type="text/javascript" src="/browserify.js"></script> <script type="text/javascript"> var foo = require('./foo'); window.onload = function () { document.getElementById('result').innerHTML = foo(100); }; </script> </head> <body> foo = <span id="result"></span> </body> </html>

Then fire up the server (node server.js) and load up http://localhost:9797/

Amazing! Browserify just hosted all of those files at /browserify.js and made require() work just like in node.js!

But wait! There's more! You can specify npm modules to use and then you can require() those too! Just pass the "require" parameter to browserify:

server.js

var connect = require('connect'); var server = connect.createServer(); server.use(connect.static(__dirname)); server.use(require('browserify')({ require : [ 'traverse' ] })); server.listen(9797);

Then spew out some HTML:

index.html

<html> <head> <script type="text/javascript" src="/browserify.js"></script> <script type="text/javascript"> var Traverse = require('traverse'); window.onload = function () { var obj = { a : 1, b : [ 2, 3, 4 ], c : [ { d : 5, e : 6 } ] }; var leaves = Traverse(obj).reduce(function (acc, x) { if (this.isLeaf) acc.push(x); return acc; }, []) ; document.getElementById('result').innerHTML = leaves.join(', '); }; </script> </head> <body> <span id="result"></span> </body> </html>

Then fire up the server and check http://localhost:9797/

Success! It pulled out the leaves from that pesky nested data structure using traverse! If the npm modules have dependencies, those dependencies will get bundled along recursively!

Plus, browserify looks for the "browserify" field in package.json files up on npm, so you can serve up custom browser versions of packages. Here's the "browserify" part of dnode's package.json:

"browserify" : { "name" : "dnode", "main" : "./browser/index.js", "base" : "./browser", "require" : [ "dnode-protocol" ] },

With this field in the package.json and "dnode" in the "require" list, require('dnode') serves up the browser-side dnode code!

Here are some packages that have been tested with browserify:

And that's not all!

What an amazing amount of features! Check it out on github!

Want to make sure your browserified code runs on all the browsers? Check out my startup, browserling! Cross-browser testing from your browser!