Looking at the APNG spec, it seems that APNGs are PNGs which describe the frames after the first in their "extra" chunks". So it seems like it'd be feasible to create APNGs in Javascript easily by just compositing PNG headers and appending dataURIs to them. However it's not going well.
function compileAPNG (frames, width, height, fps) {
var fromCharCode = String.fromCharCode;
var CRC = fromCharCode(0, 0, 0, 0);
var pngData = fromCharCode(137, 80, 78, 71, 13, 10, 26, 10);
pngData += fromCharCode(0, 0, 0, 13) + "IHDR" + convertIntToBytes(width) + convertIntToBytes(height) + fromCharCode(0, 0, 0, 0, 0) + CRC
pngData += fromCharCode(0, 0, 0, 8) + "acTL" + convertIntToBytes(frames.length) + fromCharCode(0, 0, 0, 0) + CRC;
pngData += fromCharCode(0, 0, 0, 25) + "fcTL" + fromCharCode(0, 0, 0, 0) + convertIntToBytes(width) + convertIntToBytes(height);
pngData += fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + convertShortToBytes(fps) + fromCharCode(1, 0) + CRC;
pngData += convertIntToBytes(frames[0].toDataURL().length) + "IDAT" + frames[0].toDataURL() + CRC;
for (index = 1; index < frames.length; index++) {
pngData += fromCharCode(0, 0, 0, 25) + "fcTL" + fromCharCode(0, 0, 0, 0) + convertIntToBytes(width) + convertIntToBytes(height);
pngData += fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + convertShortToBytes(fps) + fromCharCode(1, 0) + CRC;
pngData += convertIntToBytes(frames[index].toDataURL().length) + "fdAT" + convertIntToBytes(index) + frames[index].toDataURL() + CRC;
}
pngData += fromCharCode(0, 0, 0, 0) + "IEND" + CRC;
window.open("data:image/png;base64," + btoa(pngData));
}
The CRC calcs are computationally expensive, so I tried to fudge them by setting them all to 0. Is this why Firefox doesn't accept the dataURI as an image?