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!
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.
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!
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.
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!
-
Browserify bundles with
es5-shim
by default so you can use shiny new javascript features like
Function.prototype.bind()
even if your browser doesn't have those yet!
-
You can specify post-filters on the bundled source to run your code
through javascript minifiers!
-
If you have
.coffee
files in your base directory they are parsed automatically so you can
require() them too!!
-
Browserify comes with compatability wrappers so you can
require('events').EventEmitters,
require('path'),
and process.nextTick()
just like in node.js, to name a few!
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!