# REA and Excalidraw: experiment record

14 October 2026. Supporting evidence for [The death of SaaS is a pricing argument](https://aecon.ai/blog/death-of-saas-rea-artcraft), by Joel Chan.

We investigated the official `@excalidraw/excalidraw@0.18.1` distribution using released `rea-agents@6.4.0`, then inspected the shipped implementation and wrote a bounded geometry reconstruction. The target is MIT-licensed. This was source-informed work in one agent environment, not an independent clean room, proprietary-app clone, development-speed benchmark or ownership-cost study.

## Input and executed analysis

The official npm tarball's SHA-512 integrity matched its registry metadata before extraction. The analysis selection contained the original package manifest and all 65 production JavaScript files, totalling 4,441,078 JavaScript bytes. Source maps, development builds, fonts, styles and type declarations were excluded. The environment used macOS, Node 22.22.0 and npm 11.5.1.

Executed operations used transient npm execution with package scripts disabled and no global agent registration:

```sh
npm exec --yes --ignore-scripts --package=rea-agents@6.4.0 -- rea analyze-javascript-application <selected-artifacts> --max-heap-mb 1024 --analysis-timeout-ms 60000 --json
npm exec --yes --ignore-scripts --package=rea-agents@6.4.0 -- rea trace-application-feature <evidence-and-seed.json> --json
npm exec --yes --ignore-scripts --package=rea-agents@6.4.0 -- rea trace-javascript-semantics <evidence-and-query.json> --json
```

The broad pass parsed all 65 JavaScript files with zero parse failures. Resource limits prevented complete semantic analysis of the engine chunk and entry file. A second pass supplied only the manifest, entry file and engine chunk. It identified the selected function but still reported partial/truncated coverage. This was the static application-analysis lane, not REA's separate JavaScript source-recovery lane.

The corrected feature trace matched one module-level export seed and traversed 847 nodes and 2,401 edges. This broad module reachability is not a precise function call graph. The semantic forward-influence query found the function but retained one node, zero relations and 21,100 relevant unknowns. We separately followed the export edges and read the minified shipped code. An initial invalid seed and a later cross-graph node-ID mismatch were corrected; neither was counted as discovery success.

## Original implementation and reconstruction

The public `getCommonBounds` export links to engine export `bj` and local binding `$e`. It computes the coordinate box enclosing a set of shapes. The original production engine chunk has SHA-256 `72b54e8e9b3c17c69f1dd5e40203bfaed62313bc611b86ecb8a1625a12562e51`.

The oracle used the actual distributed engine export, bundled with existing esbuild and run in Chrome through Playwright. The separately installed runtime engine was checked byte-identical to the verified tarball. The final build's dependencies were contained in the experiment's scratch installation. An independent audit caught an earlier build resolving dependencies from a shared installation; we rebuilt and reran every comparison. Dependency containment is not clean-room separation.

The reconstruction below was written after inspecting the licensed implementation. It supports immutable finite rectangles, diamonds and ellipses with nonnegative dimensions. It excludes arrows, freehand, bound text, rendering, storage, collaboration and the original version cache.

## Comparison and limits

The [result record](https://aecon.ai/resources/rea-excalidraw-results.json) contains artifact hashes, tolerances, representative control outputs and the cache probes. The comparison comprised 528 scenes: 16 fixed cases and 512 scenes generated using Python `random.Random(20261014)`. Fixed cases cover empty input and each shape at zero, quarter-turn and diagonal angles, plus zero width and point geometry. Seeded scenes contain one to eight shapes, negative/positive coordinates, arbitrary rotations and some zero dimensions. Inputs were fresh objects, with version 1.

Numerical comparison used `math.isclose` with relative tolerance `1e-9` and absolute tolerance `1e-7`. The shape-aware reconstruction had zero mismatches. A deliberately crude rectangle-envelope control differed in 448 scenes. That control was constructed after source inspection; it was not a failed first attempt or a measured real-world failure rate. The candidate and harness were fixed before running the seeded comparisons.

A separate probe mutated the same rectangle without advancing version 1. The original returned cached `[0,0,100,50]`; our stateless version returned `[100,0,200,50]`. After incrementing to version 2, both returned the new bounds. The original assumes callers maintain version discipline. This result is not a diagnosed Excalidraw defect and is outside our immutable geometry contract.

An independent agent regenerated and checked the corpus, recounted the saved outcomes, then separately replayed the unchanged candidate against the original oracle in Chrome. It reproduced all 528 comparisons, the 448 control mismatches, zero candidate mismatches and the cache difference. Browser page errors were zero. The candidate also rejected an unsupported arrow, negative width and a nonfinite angle; those are its own admission rules, not claims about original API validation.

Finite test agreement does not prove universal equivalence or full API parity. Excalidraw was already open source; this establishes one REA-assisted investigation, not REA's necessity or incremental value. No token, labour-cost, productivity, subscription-saving or market-price total was measured. Repeated candidate execution is deterministic JavaScript and needs no model inference.

## Source-informed candidate

```javascript
// Source-informed experiment using the MIT-licensed Excalidraw 0.18.1 distribution.
// This is a new mathematical implementation, not an isolated clean-room claim.
// Contract: immutable finite rectangle, diamond or ellipse records; width/height >= 0.
// No bound text, arrows, freehand paths, document state, or original cache semantics.
function reconstructedCommonBounds(elements, mode = 'shape-aware') {
  if (!elements.length) return [0, 0, 0, 0];
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  for (const e of elements) {
    if (!['rectangle', 'diamond', 'ellipse'].includes(e.type)) throw new Error('unsupported shape');
    if (![e.x,e.y,e.width,e.height,e.angle].every(Number.isFinite) || e.width < 0 || e.height < 0) throw new Error('invalid geometry');
    const halfWidth = e.width / 2, halfHeight = e.height / 2;
    const cx = e.x + halfWidth, cy = e.y + halfHeight;
    const c = Math.abs(Math.cos(e.angle)), s = Math.abs(Math.sin(e.angle));
    let extentX, extentY;
    if (mode === 'shape-aware' && e.type === 'ellipse') {
      extentX = Math.hypot(halfWidth * c, halfHeight * s);
      extentY = Math.hypot(halfWidth * s, halfHeight * c);
    } else if (mode === 'shape-aware' && e.type === 'diamond') {
      extentX = Math.max(halfWidth * c, halfHeight * s);
      extentY = Math.max(halfWidth * s, halfHeight * c);
    } else {
      extentX = halfWidth * c + halfHeight * s;
      extentY = halfWidth * s + halfHeight * c;
    }
    minX = Math.min(minX, cx - extentX); minY = Math.min(minY, cy - extentY);
    maxX = Math.max(maxX, cx + extentX); maxY = Math.max(maxY, cy + extentY);
  }
  return [minX,minY,maxX,maxY];
}
globalThis.reconstructedCommonBounds = reconstructedCommonBounds;
```

## Target licence and attribution

[Excalidraw v0.18.1 licence](https://github.com/excalidraw/excalidraw/blob/v0.18.1/LICENSE). Retained for the licensed source-informed materials above:

```text
MIT License

Copyright (c) 2020 Excalidraw

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
