Category: HTML
Jan 4, 2017

My team does a holiday card promotion each year, which gives us a chance to create something unusual. This year we decided on a “create your own holiday card” image editor idea. We’d specify a couple of backgrounds and design a bunch of “sticker” graphics, then users could arrange these how they liked and print off a foldable card. Give it a try!

For this project, I created stickerbomb, a one-command in-browser basic graphics editor. It allows you to specify backgrounds and load in as many stickers as you’d like, categorized into drawers. Users can then position and manipulate the stickers however they like. When finished, they can either print it, save it, or share a URL that causes stickerbomb to reconstuct their image.


Give it a shot by designing your own smug tech conference laptop:



The show above would be created like this:

    target: '#target',
    backdrops: [ 'images/laptop.jpg' ],
    stickers: {
        'Stickers': [
                name: 'Angular',
                src: 'images/angular.png',
                widthPercentage: 15


                name: 'WordPress',
                src: 'images/wordpress.png',
                widthPercentage: 15
        'Accessories': [
                name: 'Bag',
                src: 'images/bag.png',
                widthPercentage: 60


                name: 'Tattoo',
                src: 'images/tattoo.png',
                widthPercentage: 30

Very easy! All other options can be found on the Github page.

How it works

Stickerbomb is an HTML5 canvas library. It’s plain JS that forms a number of DOM elements as controls around a canvas where it’s watching for mouse and touch movements. Whenever an event occurs on the canvas, it checks whether the selected tool should be instantaneous or work with dragging, and then, if dragging, records the position (relative to the canvas) and angle (relative to the sticker) of the start point and current point. The position information is gathered by the tool, which then uses whatever is applicable to manipulate the layers.

image sharing dialog

Rather than grant full control over positioning, it works on whole percentages. A sticker can be 15% wide, 28% from the left, and 50% from the top, for example. Movement and changes only happen on the whole numbers. This has the twofold benefit of significantly reducing the cost of the rendering loop (as each layer from the background up must be recalculated and redrawn in order on any change to the image) and allowing for very easy reconstruction of any created image. A basic notation of the information above would be w15l28t50, for example. That makes share URLs quite simple, fully client side, and small enough to pack more stickers than you’ll ever need into a URL before length limits become an issue (something like 800).

Printing and the future

One bit of weirdness in the library is its print function. For that it does a hidden, reconstructed rendering of your image in higher resolution, opens a new window with just that image at 100% width and flipped upside down, and then opens a print dialog–which was a proper pain to get working cross-browser. Assuming the aspect ratio is 1.55 this makes for a ready-to-fold card, which was the original goal. It’s not exactly what you’d expect to happen, though, so that may be branched out as its own tool separate from “print” if any future development is done on this.

The likely outcome is that this won’t be significantly developed further, but will result in a few good basic tutorial posts. How to tell if a click landed inside the boundaries of a rotated object, maybe.

Sep 15, 2016
Update:  Now with more style.
Pokémon programming

Write functions, avoid tiny amounts of work!

The most important thing in life is to have your pokémon arranged neatly. That’s a fact. While I understand you can just rename boxes with numbers so that Box 1 is 1-30 and Box 2 is 31-60, etc., there’s still a tiny bit of basic arithmetic slowing down your sorting efforts. Unacceptable. A program needed to be typed up, immediately.

I decided it would be quicker just to write a few functions to tell me the box, row, and column for any given pokémon. Then I decided I’d like to check by name as well as dex #. Then I decided maybe it needed search with autocomplete. And then maybe some sprites… Long story short, now there’s PokéSorter.

Enter the name or dex # of a pokémon and it will show you exactly where to put it in your living dex.

Update 10/10/2016:

PokéSorter ScreenshotAfter thinking about it for a bit, I realized it might as well also let you track which pokémon you currently have in your dex or living dex, so it got an account system. Then it got sharing features, so for example my dex is at Then, why not, it got comparison features, so you can compare your dex against mine at,<your username>. Some of my friends use Google Sheets to track their pokédexes, so I went ahead and gave it CSV export as well. In the future it will probably get IV, nature, move tracking, etc.

All in all, it was good practice for creating a single-page app with a secure account system and email capabilities. In theory it can handle a significant number of users with very little investment into the server, as little is actually real-time, the client handles the overwhelming majority of the logic, and most actions happen in batches to reduce the number of times the server has to do anything. Got quite a lot of practice in vector art, too, as that logo didn’t make itself.

Jul 24, 2015

Where I work, we use TERMINALFOUR as our main content management system. It’s a real beast of a CMS targeted at higher education, written in Java, and capable of handling very large, high-traffic sites straight out of the box. The only major hangup is that its normal publish processor for outputting HTML from user content […]

Continue reading...
Apr 15, 2015

Update: The Sendy Invoicing add-on has now been added to Sendy’s API docs as a third party resource! View this add-on and other great supporting software at Our organization has needed a more user-accessible mass email solution for some time, so when we saw Sendy it was a no-brainer to pick up the dead cheap […]

Continue reading...
Fork me on GitHub