···11+# tessera-viz PNG Encoder + tessera-viz-jsoo Design
22+33+## Goal
44+55+Add PNG encoding to the portable tessera-viz library and create a thin tessera-viz-jsoo package for displaying images in the OCaml notebook.
66+77+## Architecture
88+99+Two changes:
1010+1111+1. **tessera-viz**: Add `png_of_rgba` — a minimal PNG encoder using uncompressed deflate (stored blocks). No external dependencies. Works in native, bytecode, and js_of_ocaml contexts.
1212+1313+2. **tessera-viz-jsoo**: A thin package that base64-encodes PNG bytes and formats them as a `data:image/png;base64,...` URL for display in notebook cells.
1414+1515+## Why uncompressed deflate
1616+1717+PNG requires zlib/deflate compression for IDAT chunks. Rather than adding a dependency on `decompress` or similar, we use deflate stored blocks (no compression). This produces valid but larger PNGs. For our tile sizes (~200x200 pixels = ~160KB uncompressed RGBA), the overhead is acceptable. A proper compressor can be swapped in later if needed.
1818+1919+## API
2020+2121+### tessera-viz (addition to existing library)
2222+2323+```ocaml
2424+val png_of_rgba : rgba_image -> string
2525+(** Encode an RGBA image as PNG bytes.
2626+ Uses uncompressed deflate for portability. *)
2727+```
2828+2929+### tessera-viz-jsoo (new package)
3030+3131+```ocaml
3232+val to_data_url : Viz.rgba_image -> string
3333+(** Convert an RGBA image to a data:image/png;base64,... URL
3434+ suitable for use in an <img> tag. *)
3535+```
3636+3737+## PNG format (minimal subset)
3838+3939+```
4040+PNG signature (8 bytes)
4141+IHDR chunk: width, height, bit_depth=8, color_type=6 (RGBA)
4242+IDAT chunk: zlib header (2 bytes) + stored deflate blocks + adler32
4343+IEND chunk
4444+```
4545+4646+Each chunk: length (4B) + type (4B) + data + CRC32 (4B).
4747+4848+CRC32 is computed over type+data using the standard polynomial (0xEDB88320).
4949+5050+Scanlines: each row is prefixed with filter byte 0 (None), then raw RGBA pixels.
5151+5252+## Testing
5353+5454+- **PNG encoder (Alcotest)**: Encode a small known image, verify PNG magic bytes and valid structure. Decode with Python/ImageMagick to verify correctness.
5555+- **tessera-viz-jsoo (Playwright)**: Encode an image in a web worker, post data URL to page, verify `<img>` displays.
5656+5757+## Package structure
5858+5959+```
6060+tessera-viz-jsoo/
6161+ dune-project
6262+ tessera-viz-jsoo.opam
6363+ lib/
6464+ dune
6565+ viz_jsoo.ml
6666+ viz_jsoo.mli
6767+ test/
6868+ dune
6969+ test_browser.ml
7070+ test_browser.html
7171+```