My thanks to the person(s) in the Computer Science department at Sacramento State University who preserved
the original DoodlePad page all these years
as over the years I had lost track of my original copy.
format came along circa 2001. There were some server-based drawing apps in that period (likely using ImageMagick on the
Consider the state of the art in February, 1996:
XBM provides just one bit per pixel, thus only two colors in an image.
XBM images were usually rendered as black on light gray; DoodlePad defaults to the inverse, but provides switching between
valid C code, which evidently became a security concern.)
more than once, leading to the xframes "array" at line 209; on each click on the "canvas",
the redraw() function (line 268) rendered the XBM image into the next array element, and then
that element was referred to in the canvas frame (line 355). Also, I did have to take care in building the XBM strings,
as at the time building long strings by appending to the end could be painfully slow, so each row (scan line)
is built separately, and only if it's changed, then the rows are assembled in logarithmic-ish time.
Looking back at DoodlePad after 23 years, there are a few small flaws, but mostly I'm satisfied with what
I managed to pull off. I'm not sure why I assigned methods to objects on each instantiation, rather than assigning
them once to their prototypes, but it worked. The circle drawing code is not optimal; I later discovered more
efficient algorithms that don't take a square root for each computed point, but at least mine also only computed
an octant's worth of points and extrapolated the rest. The promised "wall of fame" never materialized; I was just too
Wild West days.
I recently developed a DoodlePad emulator so people can see how the original looked and behaved. If you've checked
out DoodlePad-1996, you know that not only
can it no longer draw, but the UI doesn't even show up, since that also depended on XBM images and other features
that are no longer supported in modern browsers. Also, it seems tiny, the fixed layout having been designed at a
time when the typical PC screen resolution was 800x600 pixels.
I came pretty close to that, adding just one new method and one property to the original
xbmImage "class". But the HTML was another matter.
I knew at the outset that XBM was no longer supported, so my first thought was just to generate PNG images instead, but that turned out to be easier
said than done.
.html document if I could. So I looked at generating PNG myself. The format is well-documented,
and I figured with
Uint8Array, TextDecoder, and btoa(),
I could construct data: URIs and just return those from
xbmImage.toString() in place of the XBM strings. Piece of cake.
Then I noticed the fine print in the PNG spec that said that IDAT (pixel data) chunks had to be compressed,
specifically using the DEFLATE algorithm. I figured that would
be too much code for my little single document emulator, and also more work than I really wanted to do, but
I went and looked at the DEFLATE spec anyway. And there I discovered — eureka! — that a DEFLATE
block header could specify no compression, and just be followed by the uncompressed data. Problem solved!
I implemented that, and ran into the first problem. It was not possible to use TextDecoder
to produce a string from a Uint8Array that btoa()
could encode (to base64). There might be a character encoding out there that would work, but I could not find one
just encoded from the Uint8Array directly, and constructed a data: URI.
I plugged that into a test document, aaaaand... Firefox showed a broken image icon. Chrome and Edge, too. I wrote
a quick Go program to write out the image to a .png file, and Gimp said it was invalid. Hmm.
I pored over the code and the PNG spec, and everything seemed right, but then I saw it. Multi-byte integers
in PNG format are big-endian, but in the embedded DEFLATE header, they're little-endian. So I fixed that, regenerated
the data: URI, and tried it. Firefox showed no error. Chrome showed no error. Edge showed
no error. Gimp showed no error. And none of them showed my image, either. It seems PNG really, really wants that pixel
data compressed, whether or not it's important to you.
So, it was on to plan B, the HTML <canvas> element. A canvas can't be initialized from
HTML the way images can be, so there'd need to be an extra "render" step to write pixel data to the canvas. The most
efficient way to do that, if you're writing every pixel, is using the ImageData object.
The xbmImage.render() code you see now in
DoodlePad-2019, implemented in function
canvasRender(), is what I initially came up with.
And it didn't work.
I banged my head against that one for a while, tried a number of variations of the code, and ultimately concluded that
ctx.getImageData() and/or ctx.putImageData() must be broken,
or one of those experimental APIs that isn't fully implemented. Though I knew it would be inefficient, I rewrote
the code to use ctx.fillRect() to draw each pixel as a 1x1 rectangle.
And that didn't work, either.
At that point, it dawned on me that the problem must lie somewhere else entirely. I had been testing the canvas-based
<head> section, and the <canvas> elements in frames
within a <frameset>. I tried it with both the canvas and the code inside the
body of the same document. That worked fine. A few more experiments confirmed that it is not possible to modify
So that's how the DoodlePad emulator ended up in its present form, using tables for layout, and canvas for rendering images. I scaled up the images and added frameset-like chrome to give a better sense of how it looked back
in the day. I think as an emulation it comes pretty close to the original effect.
June 15, 2019