The visible purpose of PanoPainter is simple: give an artist a way to work inside a panoramic image instead of treating a 360-degree world as a distorted flat rectangle. The less visible part is a document system that has to preserve the artist’s work through layers, frames, history, import, export, and device input.
The first scoped architecture record used for this retrospective is the August codebase panorama. The repository itself has older commits going back to 2017, but an old commit date is not evidence that this particular explanation existed then. The technical story here follows the documented canvas architecture and later issue-level investigations.
Six faces, one editable world
The documented canvas and layer representation uses six square faces. The panoramic export is derived from that representation. The codebase map records an equirectangular target of four face widths by two face heights, producing the intended 2:1 image when the source faces are square.
This distinction separates editing storage from delivery format. The artist experiences a view into the panorama. Export presents the result in a form another panoramic viewer can consume. Neither fact removes the need to define face dimensions, layer content, and frame ownership consistently inside the document.
It also explains why a resize is not merely changing two numbers. A layer frame can own several render targets. Replacing some successfully while another allocation fails can leave a mixed document unless the operation is staged and published as one transaction. A recoverable refusal should preserve the previous usable document rather than expose a partially resized one.
History must target identity, not position
Animation makes an apparently ordinary undo problem more subtle. The frame at position one is not necessarily the same frame after a reorder. A clear or import action that remembers only an ordinal can later restore pixels to the wrong neighbour.
The September review recorded that exact class of finding. A production stroke action already carried a stable frame identity, while older clear/import actions retained positional targets. The desired correction was to resolve the original frame by identity and refuse safely if it no longer existed. The audited tracker still held related verification work open in October, so the finding should not be described as universally resolved merely because a patch or test commit exists.
The general rule is useful beyond painting. A command refers to the thing the user edited, not whatever happens to occupy the same slot later. Reordering is a presentation operation. It must not silently redefine the target of a pending inverse operation.
Replaying an edit is not a fresh user edit
Undo and redo also have their own ownership. The review traced a panorama-import redo path that could re-enter the fresh-edit history route while consuming the redo stack. The important architectural distinction is between applying a command and recording a new command. Reapplication must not destroy the history container that is currently executing it.
This is why the acceptance record asks for complete cycles with distinguishable pixels, exact undo/redo counts, missing-source failure, and stable memory accounting. A success message or a screenshot after one import cannot establish that the inverse operations preserve the same document.
GPU completion adds another boundary. A CPU render task finishing does not automatically mean a readback has completed. The asynchronous-transfer milestone addresses history, saves, exports, thumbnails, and recording together because they all consume GPU-produced data. Its open acceptance should remain visible rather than being compressed into a claim that every document workflow is finished.
An input sample can change after arrival
Apple Pencil input provides a smaller version of the same identity problem. UIKit can initially deliver estimated touch properties and later revise them. Apple’s documentation describes using estimationUpdateIndex to correlate that revision with the original sample.
The September force-revision change preserved that identity through the input queue. A late callback updates the matching active-stroke sample once and rerenders the preview without adding another point, including the corresponding dual-brush contribution. Cancellation and committed publication close that correction window.
This is more precise than smoothing all pressure values after the fact. The correction belongs to a particular sample in a particular active stream. It must not attach to an unrelated stroke that happens to reuse a position or arrive later. The source review also keeps physical stylus qualification separate from automated and simulator evidence.
These systems meet at the same user expectation: a brush mark should remain the mark the artist intended. Projection supplies the immersive canvas, but identity, transactions, and ownership are what let the artist trust it while continuing to work.
References & further reading
Primary papers and documentation related to the techniques discussed here. Research references explain the background; the article describes implementation and validation at its dated checkpoint.
Have a thought or a question?
I'd love to hear it. ↗