Skip to content

Add WebGPU graph surface plan (Track A)#307

Draft
joesobo wants to merge 50 commits into
mainfrom
plan/webgpu-graph-surface
Draft

Add WebGPU graph surface plan (Track A)#307
joesobo wants to merge 50 commits into
mainfrom
plan/webgpu-graph-surface

Conversation

@joesobo

@joesobo joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds the phased implementation plan for replacing react-force-graph with a CodeGraphy-owned WebGPU 2D renderer and custom typed-array physics engine (Track A: A0 baseline harness → A7 remove react-force-graph).
  • Every phase carries deterministic checkpoints (commands, byte-diffs, perf budgets) so progress is mechanically verifiable; the doc is written as a cold handoff.
  • Includes a research-backed Renderer Technical Notes appendix (SDF-smoothstep antialiasing, DPR/canvas config, buffer update strategy, dense-edge blending, export path, DOM-overlay-then-MSDF label strategy, memory budget at target scale).

Sibling plan: the Rust core track lives in #305 (docs/plans/2026-07-10-rust-core-plan.md); the two tracks are independent until that plan's convergence phases.

Validation

  • Docs-only change; pre-commit typecheck green.

🤖 Generated with Claude Code

…rer technical notes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 963e8c5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

joesobo and others added 2 commits July 10, 2026 09:15
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… agent-runnable harness

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@joesobo joesobo marked this pull request as draft July 10, 2026 17:10
@joesobo

joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Implementation update — A0 harness landed, pivoting to renderer replacement

The local benchmark/fixture foundation is now pushed. I’m stopping further baseline tuning and moving directly to getting the replacement graph into Open in Editor, then removing the legacy graph.

Local current-renderer numbers (Mac arm64, Chromium, 1280×720)

Fixture Nodes / edges FPS during scripted pan/zoom Settle Hover p50 Result
1k 1,000 / 3,090 13.96 3.18s 7.5ms complete
10k 10,000 / 31,089 2.03 15.69s 39.5ms complete on the first repeated pair
50k 50,000 / 156,264 >2s bounded run timed out in hover measurement

The two repeated 10k runs produced identical counts and fixture hash: sha256:e8cf16cff57cc934f1648f541981fc5e11244ce481e55eb4b70b4fd1f0f2c242.

Visual references now in the branch

Next implementation path

  1. Introduce the renderer/layout seam only where needed to mount the replacement.
  2. Land a working 2D replacement in Open in Editor against a larger indexed graph.
  3. Add interaction/label parity needed for normal use.
  4. Delete 3D, timeline, react-force-graph, and the legacy adapter.

I’ll add the next update with a screenshot/GIF from the replacement renderer running in Open in Editor and the graph size/FPS observed locally.

@joesobo

joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Owned 2D renderer is now running in Open in Editor

Pushed the first vertical replacement slice in 6f224df3c:

  • the default 2D graph is now CodeGraphy's own Canvas2D surface
  • layout/physics now runs on the new typed-array @codegraphy-dev/graph-engine
  • ordinary links, curved links, arrows/particles, node drawing, hover/picking, drag/pin/release, click/context menu, fit, zoom, coordinate conversion, and lifecycle callbacks are wired through the existing graph shell
  • the old react-force-graph-2d runtime is no longer imported by the 2D surface

Local Open in Editor validation

Validated in the Extension Development Host against an indexed seed-307 Markdown workspace with 1,000 nodes / 3,090 connections. Zoom, middle-button pan, hover tooltip, fit, and continuous layout rendering all worked in the editor webview.

Owned Canvas2D graph in Open in Editor

Owned Canvas2D interaction demo

Interaction capture (45 zoom inputs + 45-step pan):

  • 92 rendered frames over 2.279s
  • 39.93 effective FPS under scripted Playwright input pacing
  • frame time p50 21.9ms / p95 28.7ms / max 36.2ms

Production webview build passes. The primary 2D bundle dropped from the A0 baseline 886.48 KB / 247.36 KB gzip to 718.93 KB / 215.17 KB gzip. The remaining legacy 3D chunk is still isolated at 1.51 MB and is the next removal target.

Evidence and metadata are committed in f940adaf5.

@joesobo

joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Legacy graph runtime removed

Pushed 62752aeb5 — the old graph is now out of the production path:

  • deleted the 3D renderer, Three.js drawing/runtime, 3D camera helpers, fallback flow, settings row, command, and keybinding
  • deleted the legacy d3 physics lifecycle and force-graph imperative adapters (the owned surface already bypassed them)
  • removed direct dependencies on react-force-graph-2d, react-force-graph-3d, d3-force, three, and three-spritetext
  • promoted CodeGraphy-owned node/link/control contracts through the 2D runtime
  • removed obsolete tests that existed only to verify the deleted renderer/runtime

Production source compilation and the production extension/webview build pass. The dependency scan across extension source/package metadata is clean.

Bundle result

The build now emits only the owned graph webview bundle—the separate 1,506.94 KB legacy 3D chunk is gone.

Build Main bundle gzip
A0 legacy baseline 886.48 KB 247.36 KB
First owned Canvas2D slice 718.93 KB 215.17 KB
After legacy runtime removal 694.90 KB 207.32 KB

That is a 191.58 KB (21.6%) main-bundle reduction and 40.04 KB (16.2%) gzip reduction from A0, before the WebGPU implementation starts.

The plan now explicitly records d3-force, Obsidian, and react-force-graph as architecture/behavior references while keeping them out of production dependencies, and makes node-radius collision a mandatory invariant for every engine/rendering path.

Next: remove Timeline, finish stale test-contract migration, then begin the WebGPU surface against the owned engine/camera/picking contracts.

@joesobo

joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

First WebGPU graph is live in Open in Editor

Pushed the WebGPU MVP in 5655130db with evidence in 7ba0755e2.

The owned surface now runtime-probes navigator.gpu, requests a high-performance adapter/device, configures a premultiplied-alpha canvas, validates WGSL pipelines under a GPU error scope, and renders:

  • instanced antialiased SDF node circles
  • instanced link quads
  • dynamic typed-array instance uploads through queue.writeBuffer
  • links-before-nodes render ordering
  • device-loss / validation fallback to the owned Canvas2D renderer
  • Canvas2D overlay composition for current labels, icons, arrows/particles, and plugin overlays

The typed-array CPU engine remains the layout source, including its radius-based collision pass, pin/release behavior, and deterministic fixed-step force integration.

Actual GPU validation

Validated locally in VS Code 1.128.0's Extension Development Host Open in Editor with 1,000 nodes / 3,090 edges. The surface reported data-codegraphy-renderer="webgpu", the corrected run emitted zero WebGPU/WGSL validation warnings, and hiding the Canvas overlay still left the complete GPU node/link frame visible.

Composited frame:

WebGPU graph with Canvas label/icon overlay

GPU-only frame (overlay deliberately hidden):

GPU-only instanced nodes and links

Interaction capture:

WebGPU graph interaction

Scripted Playwright interaction pacing (45 zoom + 45 pan inputs): 88 frames / 2.270s, 38.33 effective FPS, p50 22.3ms, p95 29.1ms, max 38.7ms. This is an integration responsiveness number, not yet the 50k/100k throughput result—the Canvas decoration overlay still intentionally does parity work on every frame.

Production build and source compilation pass; targeted WebGPU color-contract tests pass (3/3). Current bundle is 682.43 KB / 205.00 KB gzip, still 204 KB smaller than A0 despite adding the GPU backend.

@joesobo

joesobo commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Timeline removal complete

Pushed c867d846e (feat: remove timeline graph feature): 158 files changed, 4,073 deletions.

Removed the timeline UI, extension↔webview protocol, provider/indexing/playback paths, git-history cache/analyzer, shared timeline contracts, runtime state, repository settings, export/bootstrap surfaces, and timeline-owned churn indexing/sizing. Legacy persisted churn / access-count node sizing now silently falls back to connections.

Normal graph navigation was extracted before deletion:

  • node selection opens a temporary preview
  • double-click opens permanently
  • direct Open File behavior remains unchanged

Production source now has only three intentional timelineActive: false compatibility literals required by current @codegraphy-dev/core plugin contribution contracts; there is no timeline runtime state behind them.

Validation performed locally: production TypeScript clean, production extension/webview build clean (745 webview modules), and focused file-opening coverage 19/19 passing. No CI used.

Per the latest owner direction, future performance testing is capped at 10k nodes; I will not run additional 50k/100k performance suites.

@joesobo

joesobo commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

2D-only cleanup + owned DAG constraints

Recent pushes:

  • 3fac15ca0 removes the residual executable 3D state model (mode unions/branches, z/fz/vz positions, 3D controls and three.d.ts) while preserving shape3D only at plugin/persisted-export compatibility boundaries.
  • af93fec8d / 2540a29fc migrate the surviving tests to 2D-only contracts and remove dead retired-feature helpers.
  • 300d40d27 adds CodeGraphy-owned deterministic DAG constraints for td, lr, and radialout, including deterministic cycle breaking and worker transfer of target buffers.
  • 95cf52d5f commits the Open in Editor DAG evidence.

Top-down DAG mode on the indexed 10,000-node / 31,089-edge workspace:

10k top-down DAG layout

10k DAG interaction

The 10k rAF-paced camera check remained smooth at 144.31 FPS, p95 8.3ms, max 8.7ms with worker physics dispatch off the main thread.

Local quality status after the removals:

  • extension production + test + Playwright TypeScript: pass
  • extension ESLint: pass with zero warnings/errors
  • extension Vitest: 924 files / 5,464 tests pass
  • graph-engine: 7 tests pass + typecheck
  • no react-force-graph, d3-force, Three.js, or three-spritetext package/lockfile entries remain

No CI used.

@joesobo

joesobo commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

Final local verification checkpoint

The replacement branch is now at f2a9e344b with the old graph dependencies/product modes removed and the owned stack active:

  • WebGPU instanced SDF nodes + segmented quadratic curved link strips
  • GPU camera uniforms (camera movement no longer rebuilds/uploads graph instances)
  • typed-array fixed-step physics with mandatory iterative radius collision
  • worker-hosted large-graph simulation with transferable snapshots and fallback
  • deterministic td / lr / radialout DAG constraints with cycle breaking
  • indexed CPU node picking
  • Canvas decoration/fallback layer and composited WebGPU + label/icon PNG/JPEG export
  • no 3D or timeline runtime, no react-force-graph / d3-force / Three.js dependencies

Fresh local verification (no CI):

  • Extension Vitest: 926 files / 5,469 tests passed
  • VS Code Playwright acceptance: 121 / 121 passed in 18.1 minutes
  • extension source/test/Playwright TypeScript: passed
  • extension ESLint: passed with zero errors/warnings
  • graph-engine: 8 / 8 tests passed + typecheck (including deterministic 30fps vs 120fps fixed-step equality, collision, pinning, and DAG targets)
  • production extension/webview build: passed, 702.40 KB / 209.32 KB gzip
  • final indexed Open in Editor 10k check: 10,000 nodes / 31,089 edges, actual webgpu renderer, 143.06 rAF-paced camera FPS, p50 6.9ms, p95 7.6ms, max 8.2ms
  • branch is clean and synchronized with origin/plan/webgpu-graph-surface

The acceptance harness now deliberately targets the owned interaction/decoration canvas where tests need 2D pixels or synthetic pointer dispatch; the GPU canvas remains the first composited layer.

As directed, performance testing is capped at 10k going forward. The previously committed 50k result remains historical evidence only.

@joesobo

joesobo commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

Parity + controlled performance checkpoint (f03a5d362)

Open in Editor force-slider sweep

Newly verified parity

  • graph physics is now extension-private and split into focused config, initialization, force, collision, integration, grid, and lifecycle modules; the standalone packages/graph-engine package and lockfile edge are gone
  • GPU-native node SDFs now cover circle, square, rectangle, diamond, triangle, hexagon, and star
  • themed fill/opacity/borders and selected-node outlines are encoded into GPU instances
  • directional arrowheads are now rendered by the WebGPU link pipeline (visible in the capture above)
  • viewport-capped labels/icons/plugin overlays stay aligned to the same camera at extreme zoom
  • zoom range expanded to 0.005×–64× with coordinate round-trip tests at both limits
  • real rendered-edge picking now drives click, hover information, and edge context menus; acceptance no longer cheats by dispatching to an sr-only element
  • grouped node dragging writes every dragged node back to typed physics; pin state, velocities, and shape-derived collision radii survive updates
  • live graph updates preserve camera, worker, positions, velocities, and pins

Force controls tested in actual Open in Editor WebGPU

Each slider was driven to both endpoints and every change visibly moved the layout, with zero browser errors:

Control Tested range
Repel Force 0 → 20
Center Force 0 → 1
Link Distance 30 → 500
Link Force 0 → 1

The adapter now also preserves legacy D3 damping semantics (velocity retention = 1 - stored damping).

Controlled legacy comparison (5 runs each)

Same seed-307 1k fixture, Chromium 145, 1280×720, device scale 1, same scripted pan/zoom, same machine, same forced-GC protocol. Headless Chromium uses the owned Canvas fallback, so this is a strict fallback-vs-legacy comparison—not a WebGPU headline.

Metric (median) Legacy dcb0165ec Owned fallback Change
Interaction FPS 13.54 34.73 +156.54% (2.57×)
Settle time 2,976.5ms 2,455ms −17.52%
Hover p50 7.4ms 18.8ms +154.05% regression
Retained heap 6.80MB 10.31MB +51.56% regression

I am explicitly disclosing the two regressions rather than folding unlike workloads into a misleading speedup. Raw run arrays and environment are committed at references/comparison/legacy-vs-owned-1k.json.

Actual GPU / dynamic update measurements

  • latest 10k Open in Editor: 142.98 FPS, p50 7.0ms, p95 8.5ms, GPU p50 1.0ms, overlay p50 0.9ms
  • five 9k↔10k worker/WebGPU update runs:
    • add 1,000 nodes: 326.2ms median
    • remove 1,000 nodes: 346.0ms median
    • restyle 900 nodes: 49.3ms median
    • camera zoom preserved: yes

Evidence: open-editor-dynamic-10k/metadata.json.

Current PR-level bundle versus A0: 886.48KB → 712.94KB raw (−19.58%), 247.36KB → 212.69KB gzip (−14.02%), plus removal of the separate 1.51MB legacy 3D chunk.

@joesobo

joesobo commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

Final parity verification (963e8c5b8)

All required checks are now green on the PR head:

  • GitHub CI: 35/35 checks passed
  • local extension Vitest: 931 files / 5,489 tests passed
  • local VS Code Extension Development Host Playwright: 121/121 scenarios passed in 17.8 minutes
  • extension source/test/Playwright TypeScript: passed
  • extension ESLint: passed
  • graph-benchmark: 26/26 tests, typecheck, and lint passed
  • production extension + webview build: passed (712.99KB raw / 212.67KB gzip)
  • VSIX artifact jobs passed on macOS arm64, Linux x64, and Windows x64
  • native extension runtime passed on macOS, Linux, and Windows

The Playwright CI shards were also corrected to run only the VS Code extension suite. The standalone graph benchmark remains a separate local benchmark because CI does not install Chromium's headless shell; this removed the shared infrastructure failure that had made every VS Code shard fail before any scenario ran.

The 121-scenario suite covers the active graph contexts and retained product behavior, including:

  • node/folder/multi-node/edge/background context menus
  • selection, favorite outlines, dragging, hover and file opening
  • pan, zoom in/out, press-and-hold zoom, and Fit to Screen
  • graph theme colors and directional rendering
  • graph scope/filtering across node and edge kinds
  • startup, ignored files, plugin data/icons, and all language examples

Additional owned-renderer tests cover all seven GPU node shapes, shape-derived collision bounds, fixed-step determinism, 30fps/120fps equivalence, every force slider mapping/effect, grouped force dragging, live topology/style updates, extreme zoom coordinate precision, spatial node picking, curved-edge picking, DAG constraints, pin/release, and worker fallback contracts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant