In order to view this website properly and use all its capabilities, please activate JavaScript.
Here are the instructions how to enable JavaScript in your web browser.

Generate random noise with HTML5 Canvas

By Thibaut Gilbert
new

During the development of my Mad Men Game, I had to generate a visual random noize (similar to HBO's credits). The constraint is doing this on a large canvas, like 2000 x 2000 pixels (it's always good practice to work on an even number of pixels).

A random noise consists of a bitmap of randomly spread monochromed pixels. In RGBA terms, this means that each pixel can take any value in the range 255,255,255,255 to 0,0,0,255 (we assume that there is no transparency).

In the Canvas world a simple way to handle pixels data is the ImageData interface. This image data is a one dimensional typed array containing the RGBA values. In our demo the Canvas is 2000 pixels wide by 2000 pixels height. So 4 000 000 pixels, each pixel represented by 4 components (RGBA), which give us 16 billions components.

Thus, technally speaking we have to get an image data of the canvas and randomly set RGBA values into it. Easy, but the 1 million euros question is : "how to iterate thru the 16 billions items of the data array efficiently?"

When you're drawing onto a Canvas, your computations shouldn't last more than 16ms, so you should definitely optimize your loops...

First attempt, intermediary Array

With this in mind I took the approach that seemed the most efficient to me at first. Global variables are:

  • ctx: Canvas 2D context
  • width: canvas width
  • height: canvas height

As my concern was to reduce the number of iterations, I used a for loop with a pace of 4 items and push 4 components at a time. Awesome, but unfortunaltely the typed array (Uint8ClampedArray interface) which contains the RGBA values doesn't have any method. So I have to use an intermediary Array, components.

var imageData = ctx.getImageData( 0, 0, width, height ),
    n = imageData.data.length,
    components = [], tmpComponent;

// first iteration, shorter by a factor 4, cooool baby
for ( let i = 0; i < n ; i += 4 ) {
  tmpComponent = Math.floor( Math.random() * 256 );
  components.push( tmpComponent, tmpComponent, tmpComponent, 255 );
}

// second iteration, full length... OMG..
components = Uint8ClampedArray.from( components );

imageData.data.set( components );
ctx.putImageData( imageData, 0, 0 );

Well, this solution is terribly slow.

Using the console.time and console.timeEnd functions, I pinpointed the longest instructions. The first bottleneck is the use of getImageData(). The function copies each pixels on the Canvas into a Uint8ClampedArray array (wrapped in the imageData object). But we don't really care of those initial pixels. Instead it's better to create a "transparent" array (all components equals to 0) via the createImageData() function. I guess this function is faster because it doesn't request the Canvas to get pixels values.

Another big issue came from calling the Math object static functions (random and floor). I created a JSPerf test to check whether Math functions should be called from Math object or globally. Results aren't obvious but I decided to take the global approach.

Finally the loooooongest instruction is the call of the from() method. Indeed, this method has to parse AGAIN ALL the components array in order to clone it!! So my code is finally slower than a full iteration on the inital imageData.data array :(

Second attempt, loop directly on Uint8ClampedArray

So I went back on my thoughs and, inspired by this GIST, I refactored my code.

Appart from the previous improvments, the genious in Don Park's code lies in the serialized affectations to the components array. In only one line (which doesn't use any function call) we can update 3 contiguous items. Another good trick is the | binary operator which allows to get rid of the decimal part of the random call way faster than floor() : https://jsperf.com/jsfvsbitnot/8.

// js
var imageData = ctx.createImageData(width, height),
    components = imageData.data,
    n = components.length,
    window.random = Math.random,
    i = 0;


while (i < n) {
  components[i++] = components[i++] = components[i++] = (random() * 256) | 0;
  // alpha
  components[i++] = 255;
}

ctx.putImageData( imageData, x, y );

Right is a chart showing the second attempt: as we say in French, Y'a pas photo!

Last solution, no intermediary array. x axis is viewport width, y axis is ellapsed time in ms

However the Park solution is faster, this could be improved ( at a 2000px viewport width it takes 92ms ).

I decided to divide the number of iterations by two by parsing the components array by both ends simultaneously and copying the same random value (so we stick to one call to random() per iteration)

// js
var n = components.length / 2,
    i = 0, j = components.length - 1,
    r;

while (i < n) {
  r = (random() * 256) | 0;
  components[i++] = components[i++] = components[i++] = r;
  components[i++] = 255;
  components[j--] = 255;
  components[j--] = components[j--] = components[j--] = r;
}
Dealing with performance could be tricky as there is no bulletproof solutions, but as the slightest change in your code can greatly improve the overall speed of your app, it's worth spending an hour on it.

Latest See all →

Parallax scrolling: why a fixed background?
new

This post explains the use of a fixed background-image in parallax scrolling. To do so I review the visual concepts behind parallax and illustrate it with the Skrollr library and a few lines of Scss.

Building an audio player with CSS Flexbox and JavaScript Promises
new

A step by step tutorial to learn how to build an HTML5 audio player from scratch, focused on:

  • the use of CSS Flexbox for a simple responsive layout,
  • an implementation of the Mediator pattern in JavaScript to develop a maintainable player,
  • the use of Promises to simplify callbacks management

About

47tibo.com is a blog focused on JavaScript, HTML5 and Ruby on Rails, offering tutorials and best practices, from intermediate to advanced.
Posts are structured around code snippets and out of the box examples, featuring various topics, from abstract to concrete ones (like performance or HTML5 features).
Source codes are under Creative Commons licence but original graphics are copyrighted.

For a presentation of all techniques at play on 47tibo.com, click here.

Connect

Liked what you've seen here?
If so you might be interested in the following: