Canvas warping with JavaScript

2013-03-10 15:12

Playing around with HTML's canvas element lately I thought about implementing a well-known warping effect with it.

warp example 1warp example 2

It's quite simple really. Iterate over all output pixels, when you are close enough to the center of the deformation, calculate the coordinates of the pixel according to the set angle and its actual distance to the center, then copy it over from source image.

Since it's an experiment, I did not bother too much with optmizing the code and preferred to keep it readable. I was surprised how fast it actually works when doing such heavy work (albeit it's fast only in Chrome, Firefox is really slow on this and Opera lagging behind Chrome a bit).

You can do cool effects with it, especially when interactive or combined with animations.

Interactive examples

Move your mouse over the canvases below:

In this example the input canvas has a grid drawn on it and we never touch it again. On mousemove event we calculate the distance from center and set that as the deformation radius. The angle from 12 o'clock to the mouse position is set as the deformation's angle. You can see that if you move left or right or pass 6 o'clock (where the angle is reversed).

Here is how it's more or less done (utilizing some helper functions):

var warp = new Warp({
        input_canvas: $('#input_canvas1').get(0),
        viewport_canvas: $('#vp_canvas1').get(0),
        top: 100,
        left: 100,

$('#vp_canvas').on('mousemove', warp, function( event ) {
        var warp =;
        var cx = 200, cy = 200;

        // calculate relative coords
        var x = event.pageX - $(this).offset().left + warp.options.left;
        var y = event.pageY - $(this).offset().top +;

                center: {x: cx, y: cy},
                radius: distance(cx, cy, x, y),
                angle: Math.atan2(cx - x, cy - y) * 180 / Math.PI,
                copy_input: true

Another interactive use:

This is an example where we have a "dynamic" input canvas, and the deformation applied is static (i.e. has all static parameters). On mousemove event on viewport canvas we calculate relative position on input canvas and place the text there. Then the deformation is applied and we get the final result on viewport canvas.

In the code below, draw_text() is a simple helper function which clears the whole input canvas and puts the text on provided coordinates.

var warp = new Warp({
        input_canvas: $('#input_canvas').get(0),
        viewport_canvas: $('#vp_canvas').get(0),
        top: 100,
        left: 100,

$('#vp_canvas').on('mousemove', warp, function( event ) {
        var warp =;
        var cx = 200, cy = 200;

        // calculate relative coords
        var x = event.pageX - $(this).offset().left + warp.options.left;
        var y = event.pageY - $(this).offset().top +;

        draw_text(warp.options.input_canvas, x, y);

                center: {x: cx, y: cy},
                radius: 150,
                angle: 45,

Animation examples

Let's try some animation on the input canvas, simply scrolling some text right to left. Warping is applied after the text is placed on input canvas.

By supplying specific deformation center and radius/angle parameters (e.g. outside the canvas, big radius, etc.) you can achieve many interesting effects.

You can also animate the center of warping, keeping all the rest static:

Picture by Carol Munro

Deformation controlling function

Warping is controlled with a function which calculates the angle of rotation at the specific pixel, according to its distance from the origin and the supplied angle.

The function takes one floating point argument from 0.0 to 1.0, being the distance from origin (0.0 is the center of warping, 1.0 the edge of it) and returns a value between 0.0 and 1.0 representing the relative angle of rotation at this distance (0.0 being no rotation at all, 1.0 being the full angle). That means you probably want a function f which satisfies the following conditions:

f(0) = 1 (meaning at the center of warping make full rotation)

f(1) = 0 (meaning at the edge of warping make no rotation at all)

Here is a dynamically-generated visual representations of various functions you could use:

The red line shows the relative angle of rotation. X-axis is the input, that is the relative distance from origin (left is center of deformation, right the edge of deformation). Y-axis shows the relative angle at this point. The top being full angle, and the bottom no rotation at all.

The gradient shows basically the same, but in a sort of top view where colors represent angles. White color means 'at this spot make full rotation' and black means 'no rotation'. As you can see, the first function is just all white and it would rotate every pixel by full angle. On the other hand, the last function starts slowly from the black and end sharply with white in the center, so it will have smooth edges of deformation.

Check out the live_examples.js file included in this post to see how the demos are made in detail.

Some more info on github and in warp.js source code.

Read more posts about:

My setup for blogging with Nikola

2013-02-27 23:51

This blog is powered by Nikola - a great, lightweight python static page generator. In this post I wanted to share the setup I use for easily and effectively blogging with it.

First, I got Nikola installed on both my local machine and my server. This might sound unusual at first since the whole point of a static page generator is that you deploy by sending just the rendered pages to your server. However, here are my considerations:

  • why would I always send all those html, css, js and possibly image files (if they change)
  • what happens if I lose my laptop for any reason

Taking that into account I came up with another solution. I don't use Nikola's built-in deploy config option. Instead, I track my posts (actually whole site) in mercurial. Then I use its hooks to actually command Nikola on my server to build it with new content and put the results in the public-facing dir.

This way I have to send only the new posts or any typo fixes over. Additionally I can keep track of my posts in mercurial, but I am not yet sure what use I will have for that.

First, setup Nikola on your server. I do it in a virtualenv inside my homedir and I use latest git version (by the way, that's one of the perks of static page generators. You can use the bleeding-edge version and still be alright. If it breaks, just fall back to an older version and regenerate your site before deploying):

~ $ virtualenv --no-site-packages nikola-virtenv
~ $ cd nikola-virtenv/
~/nikola-virtenv $ source bin/activate
(nikola-virtenv)~/nikola-virtenv $ git clone
(nikola-virtenv)~/nikola-virtenv $ cd nikola/
(nikola-virtenv)~/nikola-virtenv/nikola $ pip install -r requirements.txt
(nikola-virtenv)~/nikola-virtenv/nikola $ python install
(nikola-virtenv)~/nikola-virtenv/nikola $ ..
(nikola-virtenv)~/nikola-virtenv $ nikola init testsite
Created empty site at testsite.
(nikola-virtenv)~/nikola-virtenv $ cd testsite/
(nikola-virtenv)~/nikola-virtenv/testsite $ hg init

Here is the cool part. We setup 2 hooks in mercurial:

  • changegroup - will run when we push to the repo, and it will update the repo with the new stuff
  • update - will execute our build script

Put this in your .hg/hgrc:

changegroup = hg update >&2
update = ./

The script is really simple. It just enters the virtualenv, instructs Nikola to build and then actually deploys the resulting files to the webserver dir if everything went alright.

. ../bin/activate
nikola build
if [[ "$rc" != 0 ]] ; then
        echo "Errors building site"
        echo "Site built OK, deploying"
        cp -r output/* /<www_dir>/

Remember to make it executable by chmod +x

On my local machine I setup Nikola in a similar fashion (virtualenv, etc. Just remember to match your python versions!), then initialized mercurial repository like so:

... install Nikola like above ...
(nikola-virtenv)~/nikola-virtenv $ nikola init testsite
Created empty site at testsite.
(nikola-virtenv)~/nikola-virtenv $ cd testsite
...set everything up (theme,
...create .hgignore file (below)...
(nikola-virtenv)~/nikola-virtenv/testsite $ hg init
(nikola-virtenv)~/nikola-virtenv/testsite $ hg add

Put this in your .hgignore file:

syntax: glob

It's important to add output/* (among others) to .hgignore file so that when you build locally it doesn't pollute your repo.

In .hg/hgrc put your server config (I assume you have your ssh keys setup):

default = ssh://admin@server/~/nikola-virtenv/testsite

Finally here is the complete workflow (on your local machine):

... enter virtualenv and your blog`s dir ...

$ nikola new_post
Creating New Post

Enter title: My setup for blogging with Nikola
Your post`s text is at:  posts/my-setup-for-blogging-with-nikola.txt

... write your post ...

$ nikola serve
Serving HTTP on port 8000 ...

... check your new post in a browser ...

$ hg add posts/
$ hg ci -m "New Post: My setup for blogging with Nikola"
$ hg push

... now you should see the usual mercurial chatter along with output from
... (and thus 'nikola build'), so if you get "Site built OK, deploying"
... then you know everything went alright and your new post is live!

Just remember to keep both your Nikola installations in sync to always be sure that the draft your are seeing on your local machine will build the same on your server.

Read more posts about: