The Scene: How a Drawing Becomes Structured Data

For Excalidraw, the important question is not how to store a rectangle’s pixels; it is how to represent a visual object so that the editor, exporter, file format, and collaborators can still understand it later. This chapter examines the scene: the structured collection of elements that carries a drawing’s geometry, appearance, relationships, identity, and change history. By the end, readers should be able to look at an Excalidraw drawing as a document made of related records rather than as a picture painted onto a canvas.

The previous chapter established why that distinction matters. A scene can be rendered into a screen, serialized into an .excalidraw file, saved in a browser, or exchanged with another user because its meaning is represented before any one of those projections occurs. The scene is therefore the foundation on which the project’s visual style, portability, and collaboration behavior depend.

An element is a durable record

An Excalidraw element is not merely an instruction such as “draw a red rectangle at this point.” It is a durable record with an identity, geometry, style, and state. The common element data includes position and size, rotation, stroke and background colors, fill and stroke styles, opacity, roughness, a seed for deterministic shape generation, grouping, frame membership, links, lock state, deletion state, and metadata used to order and reconcile changes.

That list is best understood by asking what the record must survive. Position and dimensions let a renderer place the object. Style fields preserve how it should look. A stable seed lets the hand-drawn generator produce the same character instead of inventing a new wobble every time the scene is redrawn. Group and frame information preserve containment. A link or lock changes how the object behaves when a user interacts with it. Version fields allow another part of the system to decide whether an incoming copy is older, newer, or tied with the local copy.

The common base is extended by records with different relationships. Text adds the actual string, font information, alignment, line height, and a possible container. Lines and arrows add local points and endpoint bindings. Free-draw elements carry a sequence of points and pressure information. Images carry a file identifier, a loading status, a scale, and optional crop information. Frames and embeds add their own names or content boundaries. Sticky notes combine a visible container with a label whose layout depends on that container.

These are not independent shapes in a catalogue. Their relationships carry part of the drawing’s meaning. An arrow bound to a box should follow the box when the box moves. A label inside a shape should remain associated with that shape instead of becoming an unrelated text object. A frame can determine which elements are visually grouped or clipped. An image element can remain in the scene even while its binary data is being loaded, saved, or reported as missing.

A simple example shows why the distinction matters. Imagine a flow diagram with a rectangle labeled “Database,” an arrow leaving its right edge, and a small image placed beside it. The rectangle is one record. The label is another record with a relationship to the rectangle. The arrow is a linear record with a binding to the rectangle, not just a line whose endpoint happens to share the same coordinates. The image is a record that points to a separate binary file. If the rectangle is resized, those relationships tell the editor which things should move, which should be recalculated, and which should remain independent.

The record also tries to avoid storing values that belong only to a particular rendering pass. A scene element should be serializable and shareable between peers; a temporary selection box, a cached bitmap, or a browser-specific measurement does not belong there. This does not make the schema minimal. It makes the boundary intentional. Geometry needed to place text, points needed to draw a freehand path, and binding information needed to preserve an arrow’s endpoint belong to the document. A pointer hover, an open menu, or the current zoom belongs to the local editor. Keeping those categories apart is what lets a scene leave one browser without carrying accidental UI state with it.

The distinction is especially important when relationships are bidirectional in practice. A container can list the elements bound to it, while a text record can carry the identifier of its container. An arrow can point to a target, while the target can retain information about bound arrows. Such pairs can drift when files are old, partially edited, or exchanged during collaboration. Excalidraw’s restoration path therefore repairs relationships after it has enough context to inspect the whole collection. The scene is not validated one isolated object at a time; some invariants become visible only when related records are considered together.

classDiagram class Scene { ordered elements derived element maps } class ExcalidrawElement { id geometry and style seed version versionNonce fractional index isDeleted } class TextElement { text containerId } class LinearElement { points startBinding endBinding } class ImageElement { fileId status crop } class AppState { zoom selection activeTool } class BinaryFiles { file data file metadata } Scene o-- ExcalidrawElement : orders ExcalidrawElement <|-- TextElement ExcalidrawElement <|-- LinearElement ExcalidrawElement <|-- ImageElement TextElement --> ExcalidrawElement : container relationship LinearElement --> ExcalidrawElement : arrow binding ImageElement --> BinaryFiles : fileId reference Scene --> AppState : editor projection uses

The scene owns the collection and its derived views, but not every value that appears while a person edits. AppState includes the viewport, zoom, selected elements, active tool, open dialogs, and other editor conditions. Those values are essential to the live experience, yet they answer a different question: what is this user doing or seeing right now? They do not normally define the shared drawing itself.

Binary files are separate for a similar reason. An image’s element record must be portable enough to describe the image’s place in the drawing, but carrying a full data URL inside every scene operation would make ordinary editing and collaboration unnecessarily heavy. The element points to a file ID; a separate file map supplies the binary payload when the current consumer needs it. This separation also creates a meaningful failure distinction. A scene can be structurally valid while an image is pending, unavailable, or in an error state.

The result is a document model with three related layers. Elements describe the visual document. AppState describes a local editor’s working conditions. BinaryFiles supply external payloads referenced by image elements. They travel together at some boundaries, such as a local export or a host callback, but they are not interchangeable. Confusing them leads to bad assumptions about what collaboration shares, what persistence guarantees, or what a file actually contains.

That separation also gives the host application a meaningful choice about ownership. A host may persist only the elements, persist the full scene plus files, or transform the data into its own document envelope. It can restore a saved viewport for one user without making that viewport part of the shared artifact. It can load a scene with missing images and show a recovery state rather than silently replacing the image with a blank shape. The scene contract makes those choices explicit; it does not dictate one product policy.

There is a similar distinction inside the editor between a complete scene and a useful projection of it. Rendering usually wants non-deleted elements in order. Selection wants a fast lookup by ID. Frame clipping wants to know containment. A history or collaboration operation may still need deleted elements and version metadata. These are different questions answered from the same collection. Rebuilding each projection from pixels would lose the information needed to answer them, while treating one projection as the whole document would make one consumer’s optimization corrupt another consumer’s meaning.

Identity and change make the scene shareable

A structured record becomes useful across time only if the system can tell one object from another and can compare successive versions. Excalidraw gives each element an ID. That ID is not merely a React key or a convenient label. It is how a scene, a history entry, or a remote update refers to the same conceptual object after its position or appearance changes.

An element also carries a version number. When a mutation changes the element, the version normally increases, a version nonce is regenerated, and the update timestamp changes. The version says that the record has advanced. The nonce is useful when two copies report the same version: the reconciler can apply a deterministic tie-break instead of choosing according to arrival order. This does not discover the user’s intent, and it does not merge two edits field by field. It creates a repeatable rule for choosing between competing records.

Ordering needs its own representation. Array position is convenient locally, but it is fragile when two users insert or move objects at nearly the same time. Excalidraw uses fractional indices to represent order and repairs invalid or moved indices as scenes are updated. The point is not that fractional indexing solves every collaboration problem. It gives the system a stable ordering vocabulary that can travel with the elements and be checked after reconciliation.

Deletion is also part of the record rather than simply the act of erasing an object from every array. A deleted element can remain available to history or to a collaboration exchange for a period of time. That helps other clients learn that an object disappeared instead of treating its absence as an ambiguous network omission. The live editor can derive a non-deleted view for rendering while retaining a fuller scene view for operations that need history or synchronization context.

This is why a scene update is more than “the canvas changed.” A local pointer movement may update an element repeatedly while it is being dragged. Some of those changes are ephemeral interaction state; a completed edit may become a durable history entry. The store distinguishes capture modes for these cases. An update can be captured immediately for undo and redo, applied without entering local history when it comes from initialization or a remote peer, or emitted as an eventual/ephemeral change while the interaction is still in progress.

That distinction protects two different meanings of change. The renderer needs frequent updates so a dragged shape follows the pointer. History needs coherent durable steps rather than a separate undo entry for every animation frame. Collaboration needs versioned element records that can be compared with remote records. Treating all three as the same event would make at least one of them misleading: the editor would feel sluggish, undo would become noisy, or a remote update could be mistaken for a local action.

The scene class maintains several derived views to make these uses practical. It can keep the full ordered element collection, a map for ID lookup, a non-deleted collection for rendering, frame-related views, and cached selection results. When elements are replaced, it synchronizes ordering, rebuilds the relevant maps, and notifies listeners. This is an implementation of a broader architectural rule: the durable scene remains the reference point, while specialized views optimize a particular operation without becoming a second document.

The change boundary is not limited to pointer events. A programmatic host update, an imported file, a remote scene, and an undo operation can all replace or mutate scene records, but they should not all have the same history meaning. The store’s durable increments contain enough information to support undoable changes, while remote or initialization changes can be applied without pretending that the local user performed them. An eventual or ephemeral increment can tell observers that something is moving without committing every intermediate state to the durable snapshot. The design therefore keeps “the value changed” separate from “this change should become a local historical action.”

That separation has a failure benefit as well. If an update arrives for an element whose local copy is being edited, the system can retain the local object while the interaction is active instead of allowing an asynchronous message to overwrite the user’s work. If a record is newer locally, its version provides a reason to keep it. If versions tie, the nonce provides a deterministic choice. These rules do not understand whether one user’s diagram is conceptually better, but they prevent arrival timing from becoming an invisible source of divergence.

Construction and restoration enforce the same rule at the boundaries. New elements receive defaults and the metadata required for later rendering and reconciliation. Text is measured when it is created so its dimensions and anchor are part of the record rather than an accidental result of one browser paint. When imported data is restored, missing or legacy fields can be normalized, bindings can be repaired, and duplicate or unsupported records can be handled before the scene is used. The restoration layer is defensive, but it is not a guarantee that every malformed or future artifact will be accepted; it is a bounded way to turn external data into a valid scene.

The practical test is to follow one edit. A user drags the “Database” rectangle. The rectangle’s geometry changes and its version advances. The bound label and arrow may need relationship-aware updates. The scene’s ordering remains stable. The renderer will later use the new record, while history decides whether the gesture is one durable action and collaboration decides whether a remote copy should be accepted. AppState may also change because the object becomes selected, but selection is not thereby written into the rectangle’s document meaning. BinaryFiles remain untouched unless the edit concerns the image payload.

This separation is what lets the same scene support several futures. A renderer can turn the records into a hand-drawn view. A serializer can preserve them as an open document. A browser store can save them independently from image bytes. A collaborator can compare versions and ordering. None of those consumers needs to infer the entire drawing from pixels, and none needs to own every local editor condition.

The next question is how these stable records become a responsive visual surface. The answer depends on preserving the scene’s meaning while rendering it through separate static and interactive paths, deterministic hand-drawn generation, and caches that are useful for the viewport but not identical to the document itself.

References