A self-aware control panel for the web. Drops into any Three.js scene, 2D canvas, or DOM page — auto-detects what's there, mounts the right controls, and stays out of the way until you press Shift+D.
Ghost Panel is a single function — createGhostPanel(options) — that walks your project on load, registers every named mesh / light / camera / DOM adapter it finds, and surfaces the right controls on selection.
It's framework-agnostic at the core, ships first-party adapters for React, Solid, Svelte, and Vue, and has a UMD bundle for plain <script> tags. Three.js is an optional peer dependency.
Not published to npm yet — install from GitHub:
npm install github:epun/ghost-panel
npm install three # peer dep, only if you're using Three.js
Or load the UMD bundle directly:
<script src="https://cdn.jsdelivr.net/gh/epun/ghost-panel@main/dist/ghost-panel.umd.js"></script>
Vite: add three to resolve.dedupe so your app and Ghost Panel share one Three.js instance — without it you get a duplicate-instance warning and instanceof checks that fail across the boundary.
// vite.config.js export default { resolve: { dedupe: ['three'] } };
Pass your scene + camera + renderer. Ghost Panel auto-detects 3D, registers everything in the scene, and mounts the right workflows.
import { createGhostPanel } from 'ghost-panel'; const ui = createGhostPanel({ scene, camera, renderer, title: 'Inspector', });
Open localhost. Press Shift+D to toggle panels. Click any object to inspect it. Shift+A opens the add menu.
The core ESM is framework-agnostic. First-party adapters wire lifecycle for every popular front-end. Pick yours:
Every method on the ui handle returned by createGhostPanel(options). Options are documented inline.
| Option | Type | Description |
|---|---|---|
| scene | THREE.Scene | Auto-detects 3D workflow + scans for objects. |
| camera | THREE.Camera | Used by the click-to-select raycaster and gizmo. |
| renderer | THREE.WebGLRenderer | Source of the canvas for pointer events. |
| controls | OrbitControls | Optional — focus / POV camera switches sync target here. |
| workflow | string[] | Force-enable specific workflows (skips auto-detection). |
| workflowOpts | object | Per-workflow config — background, backgroundTargets, tracks, uniforms, etc. |
| title | string | Inspector panel title (default 'Inspector'). |
| scenePanel | boolean | Show the left scene/outliner panel (default true). |
| visible | boolean | Initial visibility (default true). |
| liquidGlass | boolean | Apple-style backdrop blur skin. |
Folder with addSlider, addColor, addCheckbox, addSelect, addCurveEditor, addVec3, addDial, addButton, addTrigger, addSequence, and more. Every value control auto-records undo.addFolder call).{ additive: true } to shift-click style add it to the multi-selection.THREE.Group. Children keep their world transform. Pushes undo. Bound to ⌘G._group. Bound to ⇧⌘G..clone(); plain 2D shapes deep-clone the bag-of-numbers.{ undo(): void, redo(): void, label?: string, coalesceKey?: string }. Cmd+Z / Cmd+Shift+Z are wired by default.Point Claude Code — or any MCP client — at a running panel and let it read the scene and drive it. The bridge is opt-in, loopback-only, and dev-only: nothing is exposed unless you ask for it.
1. Start the bridge. It prints a token.
npm install @modelcontextprotocol/sdk # optional dep, server-side only
npx ghost-panel-mcp
2. Attach it in your dev build.
import { attachMCPBridge } from 'ghost-panel/mcp-bridge'; const ui = createGhostPanel({ scene, camera, renderer }); if (import.meta.env.DEV) { attachMCPBridge(ui, { token: 'paste-from-the-server' }); }
3. Register it with your client.
claude mcp add ghost-panel -- npx ghost-panel-mcp
Your agent now has seventeen tools against the live page.
describe_skills · suggest_skills · get_scene_tree · get_object · list_materials · get_panel_state · get_diagnostics · screenshotselect_object · set_transform · set_control · apply_skill · assign_material · set_camera · focus_object · undo · redoEvery write goes through the same public API a click would, so an agent's edit lands on your undo stack and Cmd+Z takes it back. Nothing ships executable code into the page — register_skill is deliberately absent from the MCP surface, because a skill carries apply() and teardown() function bodies and registering one remotely would be arbitrary code execution in your browser.
ui.mcp: connected, readOnly, url, dispose().| Option | Type | Description |
|---|---|---|
| url | string | Bridge origin (default http://127.0.0.1:7391). Loopback only — anything else throws. |
| token | string | Shared token printed by the server. Omit only when it runs with --no-token. |
| readOnly | boolean | Serve reads and refuse every write. Inspection without control. |
| confirm | function | (tool, args) => boolean. Called before each mutating tool; return false to veto it. Reads are never gated. |
The transport is Server-Sent Events down and fetch back up — both native to the browser — so the library still ships with no runtime dependencies. Only the Node server needs the MCP SDK, and it's an optional peer, so an ordinary install never pulls it in.
Five runnable examples. Each one boots Ghost Panel against a different host: