Chapter 31 Web and DOM Renderers
Four CNA identities render XNA-style sprites in a browser without exposing CNA’s WebGL renderer contract. CANVAS paints pixels into a Canvas2D context. HTML_DOM represents sprites as pooled, CSS-transformed elements. SVG_DOM emits SVG image nodes and filters. PIXIJS uses the pinned PixiJS 7 UMD runtime over the browser’s own rendering choice. All are Emscripten-only and 2D-only, but their browser object models and evidence differ sharply.
| Identity | Browser representation | Additive blend | RT / MRT / query |
|---|---|---|---|
| CANVAS | CanvasRenderingContext2D pixels | reported true | yes / no / no |
| HTML_DOM | pooled div elements with CSS transforms | browser-dependent plus-lighter | yes / no / no |
| SVG_DOM | SVG image nodes and feColorMatrix | browser-dependent plus-lighter | yes / no / no |
| PIXIJS | PixiJS sprites and render textures | Pixi blend mode | yes / no / no |
31.1 CANVAS: immediate raster operations
The renderer obtains canvas.getContext(’2d’) and maps texture uploads and sprite operations onto drawImage, putImageData, and fillRect. A 2D render target is another Canvas2D surface. There is no shader stage, depth buffer, MRT, occlusion query, or 3D path; inherited effect-aware draws terminate in the explicit 3D refusal.
Only additive blending is reported among the 13 capability flags. That narrow switch is more useful than the renderer contract’s default-true policy because callers do not have to discover the 2D ceiling by constructing unsupported objects.
31.1.1 Built tests are not registered tests
Canvas test executables exist, but the CMake files deliberately do not register them with CTest. A green host test run therefore says nothing about Canvas pixels. The meaningful path is an Emscripten build executed in a browser with observable Canvas output.
Node alone is not a substitute: it supplies neither a browser canvas nor the rendering event loop expected by the module. Headless Chromium can provide those APIs, but only if the test actually opens the page, drives frames, and reads pixels or a screenshot.
31.2 HTML_DOM: sprites as pooled CSS objects
HTML_DOM does not rasterize every sprite into one surface. It maintains a pool of DOM elements, assigns image content, and expresses destination position, scale, rotation, origin, opacity, crop, and visibility through CSS and element properties. Reusing nodes avoids unbounded allocation as the visible sprite set changes frame to frame.
This representation makes browser layout and compositing part of the renderer contract. Ordering becomes DOM stacking order, transforms use CSS conventions, and additive blending depends on browser support for plus-lighter. The renderer therefore cannot promise one identical native blend implementation across browser versions.
Its off-screen target is DOM-backed, not a GPU framebuffer. MRT and queries are absent. The inherited 3D route reaches a local unconditional throw and bypasses the common WarnAndStub policy, so diagnostic configuration cannot turn that refusal into a warning-only placeholder.
31.2.1 The strongest browser evidence in this cluster
HTML DOM has a host contract target and a real Playwright/Chromium CI path under Xvfb. This is stronger than merely compiling Emscripten output because it executes the browser object model. It still matters what the script observes: node count and style strings prove a different layer from final composited pixels.
31.3 SVG_DOM: retained vector structure
SVG_DOM creates SVG image nodes and uses SVG transforms for sprite placement. Tinting is expressed through a real feColorMatrix filter rather than by rewriting texture pixels. Additive sprites set mix-blend-mode: plus-lighter; the capability query returns the running browser’s CSS.supports answer, just as HTML_DOM does. The retained tree is inspectable and naturally matches vector composition, but it inherits SVG’s filter, sampling, blending, and browser-layout semantics.
Like HTML DOM, it owns a DOM off-screen target, has no MRT or queries, and its 3D refusal bypasses WarnAndStub. Its browser smoke page checks raw CSS support, the public capability answer and the emitted sprite style together, and separate screenshot fixtures cover pixels and scissor order. Its verification tier nevertheless remains weaker: the native host target is behind an off-by-default CNA_BUILD_SVG_DOM_HOST_TESTS option, and no CI workflow drives either it or the browser pages. A runnable browser script is a real test route, not evidence that the route currently gates changes.
31.4 PIXIJS: browser library objects and render textures
PIXIJS is Emscripten-only and resolves a pinned PixiJS 7.4.2 UMD artifact with a recorded SHA-256; automatic download is on unless CNA_PIXIJS_ROOT supplies the file. It maps textures, SpriteBatch/SpriteFont draws, transforms, scissor, blend state and one active RenderTarget2D to PixiJS objects. The browser library may use WebGL internally, but the CNA identity remains PIXIJS and its contract is the 2D adapter, not CNA’s WEBGL2 family.
MRT, mip levels above zero, 3D, custom/compiled effects, cube and volume textures, cube targets and occlusion queries are explicit refusals. Render textures make its off-screen route stronger than NanoVG’s, while more than one simultaneous target still throws. Browser execution must prove the resolved library, object lifetime and final pixels; a successful Emscripten link alone does not.
31.5 The removed ASCII identity
Older editions paired Canvas with an ASCII renderer. That public identity and its selector are gone. The reusable quantization logic now lives in CNA::Graphics::AsciiPostProcessEffect; Appendix E records the current CNAEXT surface. Historical architecture is not a fourth row in the live renderer registry.
31.6 Comparison by object model
The four families can display an identical sprite scene while failing in different places:
-
•
Canvas failures cluster around pixel upload, context state, clipping, and immediate compositing;
-
•
HTML DOM failures cluster around pooling, CSS transforms, stacking, and browser blend support;
-
•
SVG DOM failures cluster around retained-node lifetime, SVG transforms, filters, and sampling.
-
•
PixiJS failures cluster around UMD artifact resolution, Pixi object/render-texture lifetime, batching, and the browser driver selected below the library.
A useful cross-renderer scene therefore includes crop, origin-centred rotation, alpha and additive overlap, clipping, target bind/unbind, and node-count stability over several frames. Inspect both structure and final pixels: the DOM can look mechanically correct while the browser composites a different color, and a correct screenshot can hide unbounded node growth.
31.7 Deployment checklist
Record the CNA identity, Emscripten version, browser and browser version, device-pixel ratio, canvas/SVG dimensions, and whether execution was visible or headless. Confirm that the page reaches multiple animation frames and that the observable is meaningful for the feature under test. Browser compilation is only the first gate; these renderers exist in the DOM that runs after it.