My new and improved server-error page

I saw an excellent article recently describing how to implement the fire effect seen in the trailer for the N64/PlayStation ports of the DooM game. I figured this would be neat to put on the page displayed whenever there's a server error. I already have an awesome 404 page, and now I'm equally happy with the 500 page.

I made a few small modifications to the code presented in the article. I'm using a back-buffer that is half the size of the canvas, then relying on canvas APIs to scale-up the resulting image. I'm also using the canvas fillText API to add text to the foreground of the canvas.

Check out the effect:

And here is the code:

function Fire(canvas_id, width, height, scale_factor) {
  var scale_factor = (scale_factor ? scale_factor : 2),
      width = (width ? width : 800) / scale_factor,
      height = (height ? height : 600) / scale_factor,
      canvas_id = canvas_id,
      fire_data = [],
      palette = [];

  var fire_colors = [
    0x07,0x07,0x07,
    0x1F,0x1F,0x1F,
    0x2F,0x0F,0x07,
    0x47,0x0F,0x07,
    0x57,0x17,0x07,
    0x67,0x1F,0x07,
    0x77,0x1F,0x07,
    0x8F,0x27,0x07,
    0x9F,0x2F,0x07,
    0xAF,0x3F,0x07,
    0xBF,0x47,0x07,
    0xC7,0x47,0x07,
    0xDF,0x4F,0x07,
    0xDF,0x57,0x07,
    0xDF,0x57,0x07,
    0xD7,0x5F,0x07,
    0xD7,0x5F,0x07,
    0xD7,0x67,0x0F,
    0xCF,0x6F,0x0F,
    0xCF,0x77,0x0F,
    0xCF,0x7F,0x0F,
    0xCF,0x87,0x17,
    0xC7,0x87,0x17,
    0xC7,0x8F,0x17,
    0xC7,0x97,0x1F,
    0xBF,0x9F,0x1F,
    0xBF,0x9F,0x1F,
    0xBF,0xA7,0x27,
    0xBF,0xA7,0x27,
    0xBF,0xAF,0x2F,
    0xB7,0xAF,0x2F,
    0xB7,0xB7,0x2F,
    0xB7,0xB7,0x37,
    0xCF,0xCF,0x6F,
    0xDF,0xDF,0x9F,
    0xEF,0xEF,0xC7,
    0xFF,0xFF,0xFF
  ];

  // Seed palette with fire colors.
  for (var i = 0, l = fire_colors.length / 3; i < l; i++) {
    palette[i] = {
      r: fire_colors[i * 3 + 0],
      g: fire_colors[i * 3 + 1],
      b: fire_colors[i * 3 + 2]
    };
  }

  function init() {
    // Initialize array representing the entire canvas.
    // Set all pixels to palette index 0 (0x07, 0x07, 0x07).
    for (var i = 0; i < width * height; i++) {
      fire_data[i] = 0;
    }

    // Set bottom line to index 36, white.
    for (var i = 0; i < width; i++) {
      fire_data[(height - 1) * width + i] = 36;
    }
  }

  function spread_fire(src) {
    var pixel = fire_data[src];
    if (pixel == 0) {
      // Set pixel above to black.
      fire_data[src - width] = 0;
    } else {
      var randIdx = Math.round(Math.random() * 3.0) & 3;
      var dst = src - randIdx + 1;

      var decrement = (Math.round(Math.random() * 2) < 1) ? 1 : 0;
      fire_data[dst - width] = pixel - decrement;
    }
  }

  function generate_fire() {
    for (var x = 0; x < width; x++) {
      for (var y = 1; y < height; y++) {
        spread_fire(y * width + x);
      }
    }
  }

  // drawing routine
  this.draw = function() {
    // Step the fire.
    generate_fire();

    color = ctx.getImageData(0, 0, width, height);

    for (var y = 0; y < height; y++) {
      for (var x = 0; x < width; x++) {
        var idx = fire_data[y * width + x];
        var pixel = palette[idx];

        var offset = ((width * y) + x) * 4;
        color.data[offset + 0] = pixel.r;
        color.data[offset + 1] = pixel.g;
        color.data[offset + 2] = pixel.b;
      }
    }

    ctx.putImageData(color, 0, 0);
    canvas_ctx.drawImage(buffer, 0, 0);
    canvas_ctx.fillStyle = "#b7b737";
    canvas_ctx.textAlign = "center";
    canvas_ctx.font = "30px sans";
    canvas_ctx.fillText("internal server error", width / 2, height / 2);

    // calculate timeout using level
    t = setTimeout("draw()", 30);
  }

  var buffer = document.createElement("canvas");
  buffer.width = width;
  buffer.height = height;
  var ctx = buffer.getContext('2d');

  var canvas = document.getElementById(canvas_id);
  var canvas_ctx = canvas.getContext('2d');

  // Fill canvas with black pixels.
  canvas_ctx.scale(scale_factor, scale_factor);
  canvas_ctx.imageSmoothingEnabled = false;
  ctx.fillStyle = "#000";
  ctx.fillRect(0, 0, width, height);
  init();

  return this;
}

Comments (0)


Commenting has been closed, but please feel free to contact me