freelance developer of javascript, python and more!

At the moment I’m working on a small project that has a futuristic computer theme. To make it a bit more visually exciting for the users, I wanted to have a transition between the steps of the application, so I thought it would be cool to have some kind of computer glitch effect followed by a scan line that comes down the screen to reveal the next page.

glitch.js

tldr: I’ve created a bit of JavaScript that applies a glitch effect transition to elements within a web page. Check out the source code on GitHub, go to the examples to see it in action, or skip to the downloads and usage

ImageGlitcher

With a bit of googling, I found ImageGlitcher by Felix Turner. This takes an image, puts it into an HTML5 canvas, and applies some filters to create the glitched effect. Looking at the source code, you can see that it achieves the effect via the following steps:

  1. Offset random slices of the image
  2. Offset one of the color channels
  3. Apply a matrix filter to lighten the image
  4. Overlay black ‘scan lines’ to give that crt monitor effect

This creates exactly the effect I’m looking for, but it has 2 problems:

Only Images are Supported

In my application, I want to be able to apply the glitch to the actual web page, not just an image… Easy, you might think, just call canvas.drawWindow on the with the current window and you can fill the canvas up with the rendered page. Unfortunately not! It seems that there are a load of security issues with this, so it’s restricted to authorized extensions only. Imagine if a webpage could insert an iframe containing your personal details from Facebook, or your bank, screenshot it, and upload it to a server.

This is where the awesome html2canvas project comes in. It’s a JavaScript library that can render an element into a canvas by inspecting the contents of the DOM. It doesn’t support all css effects, and can’t capture the contents of any iframes on the page, but for my application it works perfectly. Plus, the limited rendering may actually add to the glitched effect I’m creating.

The API is pretty simple, just include the script on the page, and call the html2canvas function with callback (it has to load any images that are within the element so needs to be an asynchronous function.)

html2canvas($("#targetElement"), {
    onrendered: function(canvas) {
        // the element has been rendered into the HTMLCanvasElement
        $("body").append(canvas);
    }
});

Rendering is Quite Slow

The second problem I came across was that the ImageGlitcher implementation seems to be quite slow! It’s fine for just creating one off images, but I want the effect to work as a transition between pages, so every millisecond of rendering counts. When I tried the ImageGlitcher code on an 840×840 canvas element that I’m going to apply the transition to, it took about 10 seconds to create the glitched image. This is far too long, so I jumped into the Chrome Profiler tab and started looking for the cause.

It turns out that ImageGlitcher uses a library called BitmapData, a port of the Flash BitmapData class to JavaScript. While this makes applying the required effects quite simple, it does a lot of extra work that isn’t required. Take this bit of ImageGlitcher code as an example:

//Add Scan Lines
var line = new Rectangle(0, 0, iw, 1);

for (i = 0; i < ih; i++) {
    if (i % 2 == 0) {
        line.y = i;
        outputBMD.fillRect(line, 0);
    }
}

It looks fairly innocuous; just iterate over the height of the image and fill every other line with black. However BitmapData.fillRect does the following:

this.context.putImageData(this.imagedata, 0, 0);
var rgb = hexToRGB(color);

this.context.fillStyle = "rgb("+rgb.r+","+rgb.g+","+rgb.b+")";
this.context.fillRect (rect.x, rect.y, rect.width, rect.height);
this.imagedata = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);

As you can see, it will call context.putImageData, hexToRGB and context.getImageData on each iteration of the loop. By removing the BitmapData class and working directly on the canvas context, we go from a few seconds to add the scan lines to under 10 ms!

Glitching a DOM element

So, the first step to creating a glitched version of a dom element is creating a canvas containing an image of the dom element we want to glitch using html2canvas:

// render the element onto a canvas
html2canvas(el, {
  onrendered: _glitch
});

Then we can write a function to actually apply the glitch effect (the _glitch function referenced in the previous snippet.) The full source code is on GitHub, but I’ll outline the main steps here:

Offset some slices of the image:

// create a temporary canvas to hold the image we're working on
var tempCanvas = document.createElement("canvas"),
  tempCtx = tempCanvas.getContext("2d"),

// randomly offset slices horizontally
for (var i = 0; i < numberOfSlices; i++) {

  // pick a random y coordinate to slice at
  var y = getRandInt(0, canvas.height);

  // pick a random height of the slice
  var chunkHeight = Math.min(getRandInt(1, canvas.h / 4), canvas.height - y);

  // pick a random horizontal distance to offset the slice
  var x = getRandInt(1, maxOffset);

  // calculate the width of the slice given the offset
  var chunkWidth = canvas.width - x;

  // draw the first part of the chunk
  tempCtx.drawImage(canvas,
    0, y, chunkWidth, chunkHeight,
    x, y, chunkWidth, chunkHeight);

  // draw the rest
  tempCtx.drawImage(canvas,
    chunkWidth, y, x, chunkHeight,
    0, y, x, chunkHeight);
}

Offset one of the color channels:

// get the ImageData from the original canvas
var srcData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data;

// get hold of the ImageData for the working image
var targetImageData = tempCtx.getImageData(0, 0, canvas.width, canvas.height);

// and get a local reference to the rgba data array
var data = targetImageData.data;

// Copy a random color channel from the original image into
// the working canvas, offsetting it by a random amount
//
// ImageData arrays are a single dimension array that contains
// 4 values for each pixel (r,g,b,a).
// so, by initializing `i` to a random number between 0 and 2,
// and incrementing by 4 on each iteration, we can replace only
// a single channel in the image
for(var i = getRandInt(0, 3), _len = srcData.length; i < _len; i += 4) {
  data[i+channelOffset] = srcData[i];
}

Brighten the image:

// Make the image brighter by doubling the rgb values
for(i = 0; i < _len; i++) {
  data[i++] *= 2;
  data[i++] *= 2;
  data[i++] *= 2;
}

And, finally, create scan lines by filling alternate lines with black:

// copy the tweaked ImageData back into the context
tempCtx.putImageData(targetImageData, 0, 0);

// add scan lines
tempCtx.fillStyle = "rgb(0,0,0)";
for (i = 0; i < canvas.height; i += 2) {
  tempCtx.fillRect (0, i, w, 1);
}

Now that’s done, tempCanvas contains the glitched images and it’s pretty trivial to write a transition that overlays the glitched canvas and removes it via some effect. Check out the source if you’re interested!

Usage and Examples

You can download the library from the GitHub Repo, and see some examples on GitHub Pages.

It requires the html2canvas library and, if you want to use the transition effect, a fairly recent jQuery.

To create a glitched version of an element, call the glitch function:

glitch(document.getElementById("currentContent"), {
    amount: 8,
    complete: function(canvas){
        // do something with the glitched canvas
    }
});

// jQuery plugin version
$("#currentContent").glitch({
    amount: 8,
    complete: function(canvas){
        // do something with the glitched canvas
    }
});

To replace an element with a glitched version of it, use glitch.replace or just call $.fn.glitch with no arguments:

glitch.replace(document.getElementById("currentContent"));

// jQuery version
$("#currentContent").glitch();

If you want to create a transition between two bits of content, jQuery is required to perform the animation:

$("#currentContent").glitch($("<div><p>New Content</p></div>"), {
    amount: 7,
    effect: "slide",
    complete: function(){
        // do something when the animation is complete
    }
});

A few things to be aware of:

As html2canvas has to iterate over the contents of the element when rendering it to the canvas, it stands to reason that the more complex the DOM, the longer the rendering will take. This is especially true of any images that are attached (including background images,) as they have to be re-loaded asynchronously by the browser. If there are any images from another domain, then they have to be loaded via a proxy to avoid tainting the canvas. So, it’s wise to remove any unneeded elements from the dom before applying the effect.

html2canvas scrolls the window to the top when rendering the page (see Issue 57) which is fine for my usage, but may not be ideal for you. There are 2 answers for this: store the scroll position before calling glitch, and set it back in the complete handler, or contribute a fix to the html2canvas project (I’m working on a fix on my fork!)

In the project i’m using this on the effect is triggered via a delegated click event handler (attached to a Backbone.js view,) which seems to create an error in chrome to do with the event listeners that were attached. To get around this, I deferred the call to the glitch function to the next tick.

§102 · July 15, 2012 · JavaScript · Tags: , , · [Print]

  • Saule

    cool stuff dude x
     

    SunshineDays

  • Mike Heavers

    Hi – this is awesome. I’m not entirely clear on how you replace the glitched content with the original content created before the glitch effect took place. Do you have an example of that?

  • sjhewitt

    Hi Mike, I don’t really understand what you mean, could you try explaining a different way?

  • http://www.facebook.com/alexandru.ardelean.355 Alexandru Ardelean

    Hello,

    I would like to thank and congratulate you on the work for this glitch script.

    I’ve used on http://ctf365.com [to simulate a transmission-glitch script] and I decided to put it on github.com. It’s still work-in-progress; it’s rather raw, but I think it’s a decent start to using your script.

    Here’s the link: https://github.com/commodo/public/tree/master/javascript/glitch

    Thanks

  • sjhewitt

    Awesome! I’m glad to see that it’s being put to use!

  • Al Bobinski

    Hi,

    thanks for interesting image processing ways.
    I’m writing text games with javascript, although myself not very proficient with coding.
    I’m looking for some really cool glitch text effects for HTML with js and jQuery, which would in some way simulate what Memory of a Broken Dimension does with text console. TV scan lines on the background, some text glitches etc.
    Do you do commercial work? I’d like to pay someone to help me spice up my HTML output with what I mentioned.

    Thanks!

  • sjhewitt

    Hi Al, drop me an email at si@sjhewitt.co.uk and we can discuss what you need.

  • morris

    not working for me (as of december 2016).
    It gives me an error message on chrome console:

    Uncaught TypeError: Cannot read property ’0′ of undefined
    at glitch (glitch.js:167)
    at HTMLDocument. (index.html:67)
    at fire (jquery.js:2913)
    at Object.fireWith [as resolveWith] (jquery.js:3025)
    at Function.ready (jquery.js:398)
    at HTMLDocument.completed (jquery.js:93)

  • Barry White

    Hey there – Absolutely LOVE glitch.js … I’m sorry, I know this is really dumb question, but is there a handy-enough way to integrate it into a WordPress-based project?

    I’ve been trying to figure something out but it ain’t workin!

    Would be so so grateful if you could drop me a line at barry.white@webinnovate.ie if you had a quick second.

    And either way, thanks so much for the awesome script.

    All Best

    Barry W

    Dublin, Ireland