Low-res bitmap render engine for big screens

Usage no npm install needed!

<script type="module">
  import pixelbutler from 'https://cdn.skypack.dev/pixelbutler';



Build Status NPM version Dependency Status devDependency Status

Fast, simple, low-res framebuffer rendering: at your service.

pixelbutler features both a Canvas and WebGL renderer. The canvas rendering ensures clean upscaling with 100% crispy pixels while the WebGL renderer runs easily at 60 FPS in high resolutions. Works great on modern mobile devices, too.

pixelbutler was initially hacked together for the 2014 lowrezjam by Stephen Whitmore, and has grown significantly in code quality and capabilities since thanks to Bart van der Schoor.


pixelbutler is available on npm and bower.

Since it uses UMD, it will happily work as a browser global, or as a CommonJS or AMD module. Browserify & Webpack users can also use the npm package directly.

TypeScript users can use the source using import, or by using the npm or bower packages with the dist/pixelbutler.d.ts. definition file.

Quick 'n Easy Usage

<canvas id="game" width="640" height="480"></canvas>

<script src="js/pixelbutler.js"></script>

<script type="text/javascript">
    var $pb = new pixelbutler.Stage({
        width: 160,
        height: 120,
        canvas: 'game'

    // colours are in RGB format as a map with keys 'r', 'g', and 'b'.
    var red = {r: 255, g: 0, b: 0};
    var green = {r: 0, g: 255, b: 0};
    var blue = {r: 0, g: 0, b: 255};
    var black = {r: 0, g: 0, b: 0};
    var white = {r: 255, g: 255, b: 255};

    setInterval(function () {

        $pb.fillCircle(80, 60, 48, green);
        $pb.fillRect(50, 30, 60, 60, blue);

        for (var i = 0; i < 15; i++) {
            $pb.setPixel(pixelbutler.rand($pb.width), pixelbutler.rand($pb.height), red);

        $pb.text(5, 5, "pixelbutler: serving you awesome", white);

    }, 30);

Browse more examples!



pixelbutler.rgb(r, g, b)

Colours take the form { r: 100, g: 200, b: 255 } or pixelbutler.rgb(100, 200, 255).

Values range from 0 - 255.


$fb = new pixelbutler.Stage({ width: 120, height: 160, canvas: 'canvasId', center: true, scale: 'fit' })

Creates a new framebuffer object with the given width and height. This assumes you already have a canvas element in your DOM with id canvasId. The framebuffer will stretch to fill the canvas, so selecting the correct aspect ratio is left up to the user. The resulting framebuffer object supports the following operations:


Sets all pixels to the colour rgb.


Draws the state of the framebuffer to the canvas.

$fb.setPixel(x, y, rgb)

Safely (ignoring any out-of-bounds coordinates for you) draws a single pixel at coordinates x,y of colour rgb.


$fb.drawRect(x, y, width, height, rgb)
$fb.fillRect(x, y, width, height, rgb)

Draws a filled or unfilled rectangle at x,y with the given width, height and colour rgb.

$fb.drawCircle(x, y, radius, rgb)
$fb.fillCircle(x, y, radius, rgb)

Draws a filled or unfilled circle at x,y with the given radius and colour rgb.


$fb.text(x, y, txt, rgb)

pixelbutler includes a built-in low res 4x4 font that's ready to be used out of the box.


var sprite = new pixelbutler.Bitmap(width, height)

Allocates a widthxheight offscreen buffer that functions not unlike the framebuffer itself.

sprite.setPixel(x, y, rgb)

Does bounds checking.

$fb.blit(sprite, x, y, width, height, sourceX, sourceY)

Draws a sprite to the framebuffer at the given x,y coordinates.

width and height are used if present, but default to the full size of the sprite.

sourceX and sourceY refer to where within the source sprite the blit begins, where (0,0) is the top left of the image.


pixelbutler supports software shaders!


This runs an arbitrary function across all of the framebuffer's pixels, modifying the framebuffer immediately.

The function provided should have the form function(x, y, rgb). Its return value is the final colour the pixel at x,y will take.

e.g. grayscale shader

$fb.shader(function(x, y, rgb) {
  var hsv = pixelbutler.hsv(rgb);
  return pixelbutler.rgb(hsv[0], 0, hsv[2]);

Shaders can also be chained, creating pipelines.

var invert = function(x, y, rgb) {
  return pixelbutler.rgb(255-rgb[0], 255-rgb[1], 255-rgb[2]);

var halfBrightness = function(x, y, rgb) {
  var hsv = pixelbutler.rgb2hsv(rgb);
  hsv[2] *= 0.5;
  return pixelbutler.hsv2rgb(hsv);

var pipeline = function(x, y, rgb) {
  return halfBrightness(x, y, invert(x, y, rgb));



pixelbutler provides a few helper methods for manipulating colour.


Generates a random integer between 0 and n.


Converts a rgb value to an hsv value.


Converts an hsv value to an rgb value.


The project is written in TypeScript, and built for browsers using grunt and webpack. Development tools run on node.js and are pulled from npm.

To regenerate the bundles use the following steps:

  1. Clone the git repos to you local machine.

  2. Make sure you have the global grunt command:

$ npm install grunt-cli -g
  1. Install development dependencies from npm:
$ npm install
  1. Rebuild bundles using grunt:
$ grunt build
  1. Watch tasks to auto-build during development:
$ grunt watch
  1. Run a local test server for the demo's and tests:
$ grunt server

See the Gruntfile.js and $ grunt --help for additional commands.


..are very welcome. Try to stay consistent with existing style, and make sure to run grunt test before sending a pull request.


Copyright (c) 2014 Stephen Whitmore & Bart van der Schoor

Licensed under the MIT license.