I wrote a small node app that watermarks images as an inside joke between friends, and I learned some pretty neat tricks along the way. The app will take a URL to an image and overlay a gigantic hand over it. On the useful scale, it is hovering at around a 3 (someone must have a use for this somewhere, right?) but it took a lot of Googling to make this work.
One of the big challenges for this was that I wanted to host it on heroku, and being a read-only filesystem I had to process all the images without touching the filesystem. Finding out how to do this in node was surprisingly difficult at the time.
I ended up getting around this by just streaming straight through using the gm
and request
npm modules. One thing to keep in mind is that since we’re on heroku we need to use ImageMagick, we can do that in gm with imageMagick = gm.subClass({imageMagick: true})
.
The gnarly details
Once we have imageMagick
we can start the watermarking process. Each request that comes in will take the following steps:
- Find which “hand” we want to use (the “type” param)
- Open up the
sourceUrl
(the “url” param) using therequest
package - Stream that into
imageMagick
- Resize the hand to match the source images’s dimensions
- Overlay the hand
- Stream the whole thing back to the use as a PNG
// Partial code from: https://github.com/robhurring/bighand/blob/master/controllers/hand.js
var imageMagick = gm.subClass({imageMagick: true});
// 2 & 3. stream the sourceUrl into imageMagick
imageMagick(request(sourceUrl)).size(function(err, size){
if (err) return next(err);
// tell the hand the source image's dimensions and ask it what size to be
var resizeTo = hand.size(size.width, size.height);
// 5. overlay the hand we want (NOTE: we make the request twice, not optimal)
imageMagick(request(sourceUrl))
// 4. resize the hand to fit the image's dimensions
.overlayHand(hand, resizeTo)
// 6. Stream it all back to the user
.stream('png', function (err, stdout) {
if (err) return next(err);
res.setHeader('Expires', new Date(Date.now() + 604800000));
res.setHeader('Content-Type', 'image/png');
stdout.pipe(res);
});
});
// 5. helper to overlay the hand
imageMagick.prototype.overlayHand = function(hand, resize) {
return this.gravity(hand.gravity)
.out('(', hand.path, ' ', '-resize', resize, ')')
.out('-composite');
}
each hand is pulled from a config file and has the following properties:
module.exports = {
hands: {
'hand': {
description: 'Just a hand',
path: 'hands/hand.png',
gravity: 'SouthWest',
size: function(width, height) {
// how to resize ourself
return width + 'x' + height + '^';
}
}
}
// ... more hands
};
Some (goofy) Screenshots
Here is the main app:
Main app The main app
And of course the hands are customizable!
Overall, not incredibly useful as a hand-overlaying app, but you may need to have an image watermarking service in your project and this could come in “handy.”