Jank-free rendering on canvas

A failed attempt at jank-free rendering on HTML5 canvas.

by Michael Prosser

HTML

<body>
  <canvas id="screen" width="800" height="600"></canvas>
</body>

CSS

canvas {
    width: 800px;
    height: 600px;
  }

JavaScript

// A simple utility for executing multiple tasks within one requestAnimationCallback.
// Priority tasks are executed before idle tasks. The utility makes it easier to
// split long-running rendering tasks into batches whose duration does not exceed
// the deadline provided in the parameter.
var RequestAnimationFrameManager = function(deadlineMs) {
  var priority = [];
  var idle = [];
  var animationFrameRequested = false;

  this.schedulePriorityTask = function (task) {
    priority.push(task);
    schedule();
  };

  this.scheduleIdleTask = function (task) {
    idle.push(task);
    schedule();
  };

  function schedule() {
    if (!animationFrameRequested) {
      requestAnimationFrame(processQueues);
      animationFrameRequested = true;
    }
  }

  function processQueues(now) {
    animationFrameRequested = false;
    var i, task;

    var started = window.performance.now();
    var deadline = started + deadlineMs;

    var priorityTasksToRun = priority;
    var idleTasksToRun = idle;
    priority = [];
    idle = [];

    execute(priorityTasksToRun, priority);
    execute(idleTasksToRun, idle);
    if (priority.length > 0 || idle.length > 0) {
      schedule();
    }

    function execute(tasks, nextTasks) {
      for (i = 0; i < tasks.length; i++) {
        task = tasks[i];

        var taskStart = window.performance.now();
        if (taskStart < deadline) {
          // Let the task know what the current deadline is.
          // It is the responsibility of the task to return
          // before the deadline passes.
          var completed = task(now, taskStart, deadline);
          if (completed) {
            continue;
          }
        }
        nextTasks.push(task);
      }
    }
  }
};

// Allow 12ms for each frame.
var rafManager = new RequestAnimationFrameManager(2);

// Set up canvas
var screenCanvas = document.getElementById("screen");
var width = screenCanvas.width, height = screenCanvas.height;

// Initialize drawing context
var screenCtx =...