Back to front page

Technical notes:

Canvex is written entirely in JavaScript, with the browser's canvas implementation doing the processing-intensive work of shuffling pixels around. The canvas is limited to 2D graphics, so all the 3D effects are built by Canvex using the 2D operations.

The engine is based on raycasting in a similar fashion to the Build engine. The world is split into sectors (convex polygons of constant floor/ceiling colour and lighting). The edges of the polygons are either solid textured walls, or portal walls leading into an attached sector. To compute a single column of pixels, a ray is cast out from the camera into the surrounding sector, and tracked through portal walls until it hits a solid wall.

The solid wall is drawn first. Tracing backwards along the path of the ray, floors and ceilings are drawn, and the textured steps above/below portal walls (when between sectors of differing floor/ceiling heights) are drawn too.

Drawing one-pixel-wide columns is slow, because it requires a lot of calls to the canvas's drawImage function. The main optimisation is to draw an entire wall at once when it is first hit by a ray: one ray is cast to find the wall at the left column of the viewport, and then the wall is drawn up to its right end (or to the end of the viewport, whichever is closer). drawImage can only handle rectangles, so the wall (which has perspective and gets smaller in the distance) is split into a number of vertical strips, typically no less than eight pixels wide.

Since the <canvas> tag is not part of the XHTML 1.0 specification which Canvex uses, gaining acceptance from validators is a little tricky. But XHTML claims to be Extensible, so it makes sense to extend it by adding elements to the DTD. The following DOCTYPE declaration keeps browsers and the validator happy, and is hopefully not doing anything too wrong:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
[
  <!ELEMENT canvas (#PCDATA)>
  <!ATTLIST canvas
    id          ID             #IMPLIED
    style       CDATA          #IMPLIED
    height      CDATA          #IMPLIED
    width       CDATA          #IMPLIED
  >
  <!ENTITY % misc.inline "ins | del | script | canvas">
]>

Various small things (bugs and features and differences) were discovered during development, particularly when comparing Firefox and Opera, and may be of interest to anyone else working in this area. Speed comparisons were done in a fairly simplistic way, so you should not trust them to give correct advice.