Doop
Doop is an open-source multiplayer design canvas and design MCP for Claude Code, Codex, and any AI agent. Design side by side with agents — free in beta.
Spacery hosts 3D captures as virtual spaces. Upload a Gaussian splat, a LiDAR or E57 scan, 360 photos or a mesh and share the space with a link.
DesignTypeScript · JavaScriptGitHub @lukehollisOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Open, hackable 3d digital twin builder that supports 3d gaussian splatting, 360 images, and vfx
Taken from the repository README
Loading the full README.
Read the full README ↗SPHR is an open source 3D space viewer you can host yourself. It is built for lidar scans, 3D Gaussian Splatting (3DGS) captures, and 360 image or video tours, and it runs entirely in the browser. Clone it, point it at your own scans, and serve tours from your own domain.
Self-host SPHR with this repository, or use paid hosting at spacery.dev.
This branch is the Next.js + Three.js migration. It removes the old separate backend requirement and ships as a client-rendered viewer that can load a default demo, a remote JSON config, or embedded bootstrap data from a host page.
https://github.com/user-attachments/assets/169c7729-e4d7-4889-99d2-42d81e33a6d8
https://github.com/user-attachments/assets/2a181774-616f-445d-8eed-7d4ed589bd88
Import a Matterport export ZIP or E57 into a complete local tour:
python3 -m venv .venv-matterport
.venv-matterport/bin/pip install -r scripts/matterport/requirements.txt
npm run import:matterport -- --e57 /path/to/export.zip
npm run dev
Open / to browse thumbnails, search, sort and copy permanent scene links. /library
redirects to this collection. See collection and hosting.
The importer derives image orientation from camera
poses, preserves meters, fuses measured depth into a mesh of at most 50,000 triangles,
and validates every scan before publishing. See the migration guide
for configuration, geometry conventions, quality checks, and output files.
The reworked SPHR is centered on 3D Gaussian Splatting. The runtime can load splat assets or 360 images, etc directly in the browser, place them in a Three.js scene, animate camera movement through the capture, and layer guided tour text, UI controls, skyboxes, annotations, models, and effects over the scene.
SPHR uses @sparkjsdev/spark for splat rendering. The current splat layer supports:
.splat, .ply, .spz, .ksplat, .sog, and .rad sourcesfetchExample splat config:
{
"space": {
"title": "Research Garden Capture",
"type": "splat",
"space_data": {
"noPanos": true,
"initialPosition": { "x": 4, "y": 1.5, "z": 4 },
"initialRotation": { "azimuth": 0, "polar": 90 },
"splats": [
{
"id": "garden",
"url": "https://your-asset-host.example/captures/garden.splat",
"fileType": "splat",
"position": [0, 2.6, 0],
"rotation": [-2.56518, -2.66973, 0.2615546],
"scale": 1,
"lod": false,
"reveal": true
}
]
}
}
}
Large splat files are intentionally not meant to live in git. Host captures in object storage, a CDN, a research artifact mirror, or a local static directory that is excluded from version control.
SPHR currently supports these runtime layers:
SparkSplatLayerPanoramaLayerIiifImageLayerSceneGraphLayerAnnotationLayerThe runtime is not a visual authoring tool. It is the viewer/runtime that renders a structured SPHR scene.
Install dependencies:
npm install
Run the Next.js dev server:
npm run dev
Open http://localhost:3000.
Build and serve a production build:
npm run build
npm run start
Run TypeScript validation:
npm run typecheck
npm run start is for production builds. Use npm run dev while developing.
To open your own capture:
.splat, .ply, .spz, .ksplat, .sog, or .rad.space.type of splat and a space.space_data.splats entry./?config=/path/to/bootstrap.json.Useful prompts for coding agents:
Find an openly available 3D Gaussian Splatting research capture with a downloadable .splat, .ply, .spz, .ksplat, .sog, or .rad asset. Confirm the license and whether the URL is browser-fetchable, then generate a SPHR bootstrap JSON that opens it.
Use the Garden scene from recent 3DGS research as the target scene. Locate or prepare a compatible splat asset, keep large binaries out of git, and wire SPHR to it through ?config with correct position, rotation, scale, and optional skybox settings.
Open my local 3DGS capture in SPHR. Inspect the asset format, serve it from an ignored local static path or external asset host, create a bootstrap JSON, and tune the camera start position until the scene opens correctly.
The homepage lists imported scenes. A /s/<sceneId>/<title-slug> route selects a package
by its saved ID and passes its config to components/SphrApp.tsx. Older title slugs
redirect to the current title. Existing query-based scene URLs remain supported.
Inside the viewer, loadBootstrapData() from lib/bootstrap.ts resolves data in this order:
?config=/path/to/bootstrap.jsonwindow.__SPHR_BOOTSTRAP__space_data, tour_data, ordered_spaces_data, and ui_data?demo=garden opens the bundled demo)The recommended integration path is a single bootstrap JSON file:
{
"space": {
"id": "my-space",
"title": "My Space",
"type": "splat",
"mesh": "/models/dollhouse.glb",
"space_data": {
"initialPosition": { "x": 0, "y": 1.6, "z": 4 },
"initialRotation": { "azimuth": 0, "polar": 0 },
"splats": [
{
"id": "capture",
"url": "/captures/scene.splat",
"scale": 1
}
],
"sceneGraph": []
}
},
"tour": {
"id": "my-tour",
"title": "My Tour",
"tour_data": {
"defaultShowText": true,
"spaces": [
{
"id": "my-space",
"title": "My Space",
"tourpoints": [
{
"viewMode": "FPV",
"targetType": "FREE",
"text": "Start here.",
"position": { "x": 0, "y": 1.6, "z": 4 },
"rotation": { "azimuth": 0, "polar": 0 },
"zoom": 0
}
]
}
]
}
},
"ui": {
"titlePart1": "SPHR",
"titlePart2": "Demo",
"enterButtonText": "Enter",
"exploreButtonText": "Explore"
}
}
The authoritative schema lives in lib/types.ts.
SPHR also supports classic 360-image tours. A 360 space is defined as a set of nodes. Each node has a camera position and either an equirectangular image, six cube faces, or a texture template that resolves to cube faces.
Minimal node example:
{
"space": {
"title": "360 Tour",
"type": "spaces",
"space_data": {
"initialNode": "scan-000",
"nodes": [
{
"uuid": "scan-000",
"image": "/panos/scan-000.jpg",
"position": { "x": 0, "y": 1.6, "z": 0 },
"floorPosition": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0 }
},
{
"uuid": "scan-001",
"faces": [
"/panos/scan-001/face0.jpg",
"/panos/scan-001/face1.jpg",
"/panos/scan-001/face2.jpg",
"/panos/scan-001/face3.jpg",
"/panos/scan-001/face4.jpg",
"/panos/scan-001/face5.jpg"
],
"position": { "x": 2.5, "y": 1.6, "z": 0 },
"floorPosition": { "x": 2.5, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0 }
}
]
}
}
}
The basic 360 transition crossfades between the outgoing panorama and the incoming panorama while the camera animates to the next node.
SPHR also includes a stronger mesh-based transition for moving between 360 images. During navigation, SphrRuntime captures the outgoing panorama into a WebGLCubeRenderTarget with a CubeCamera. That cube texture is temporarily applied to configured scene graph meshes, usually a low-poly dollhouse or scan mesh, while the main camera flies to the next node. The transition mesh fades out as the new panorama becomes active. This makes the movement feel spatial instead of just cutting between two photos.
Transition config example:
{
"space_data": {
"navigationTransition": {
"enabled": true,
"meshIds": ["dollhouse"],
"opacity": 0.2,
"meshFadeMs": 400,
"navigationMs": 1100,
"cubeRenderTargetSize": 2048
},
"sceneGraph": [
{
"id": "dollhouse",
"type": "model",
"file": "/models/dollhouse.glb",
"visible": true,
"persistent": true,
"raycast": true,
"fpvOpacity": 0,
"orbitOpacity": 1,
"transitionMesh": true,
"transitionOpacity": 0.2,
"transitionFadeMs": 400,
"transitionTexture": "cube-render-target"
}
]
}
}
For this transition to run, the space needs:
navigationTransition.enabled not set to falseSet noPanos: true for splat-only spaces where panorama rendering should be disabled.
Tours are arrays of spaces and tourpoints. A tourpoint can move the camera, switch view mode, show text, reveal models, show annotations, play sounds, or trigger runtime modes through the extra field.
Common tourpoint fields:
viewMode: FPV or ORBITtargetType: NODE, FREE, or MODELnodeUUID: node to navigate to in a 360 tourposition: free camera position for splat or free-flight pointsrotation: camera azimuth and polar angleszoom: camera FOV adjustmenttext and secondaryText: guided tour copymodels: scene graph IDs to show at this stepannotations or overlays: annotation IDs to show at this stepsounds: audio IDs to play at this stepextra: runtime mode such as nightMode, shrinkToPoints, or projectToSplatsThe scene graph lets a tour add persistent or step-specific 3D content around the main capture.
Supported node uses include:
Transforms use arrays for scene graph nodes:
{
"id": "artifact",
"type": "model",
"file": "/models/artifact.glb",
"position": [0, 1.2, -2],
"rotation": [0, 0.4, 0],
"scale": 1,
"visible": false,
"raycast": true
}
MIT, but if this is useful to you, please cite this repo because it helps the work continue.
@misc{hollis2024sphr,
author = {Luke Hollis},
title = {SPHR - Spatial Human Reality in a web browser},
year = {2024},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/lukehollis/sphr}}
}