{"id":13406499,"url":"https://github.com/musehq/spacesvr","last_synced_at":"2025-09-01T02:36:57.268Z","repository":{"id":37931621,"uuid":"311841662","full_name":"musehq/spacesvr","owner":"musehq","description":"A standardized reality for the future of the 3D Web.","archived":false,"fork":false,"pushed_at":"2023-05-11T23:51:06.000Z","size":1943,"stargazers_count":185,"open_issues_count":19,"forks_count":37,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-08-31T22:42:21.671Z","etag":null,"topics":["js","react","react-three-fiber","three","threejs","vr","webvr","webxr","xr"],"latest_commit_sha":null,"homepage":"https://spacesvr.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/musehq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-11-11T02:30:33.000Z","updated_at":"2025-07-14T17:27:27.000Z","dependencies_parsed_at":"2024-01-18T23:05:28.147Z","dependency_job_id":"abd317b0-9df0-4009-8fe6-80a8ea95751a","html_url":"https://github.com/musehq/spacesvr","commit_stats":null,"previous_names":["spacesvr/spacesvr"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/musehq/spacesvr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musehq%2Fspacesvr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musehq%2Fspacesvr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musehq%2Fspacesvr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musehq%2Fspacesvr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/musehq","download_url":"https://codeload.github.com/musehq/spacesvr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musehq%2Fspacesvr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273066585,"owners_count":25039625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["js","react","react-three-fiber","three","threejs","vr","webvr","webxr","xr"],"created_at":"2024-07-30T19:02:31.922Z","updated_at":"2025-09-01T02:36:57.249Z","avatar_url":"https://github.com/musehq.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg width=\"500\" src=\"https://d27rt3a60hh1lx.cloudfront.net/spacesvr/spacesvr.png\" alt=\"logo\" /\u003e\n\u003c/p\u003e\n\u003ch3 align=\"center\"\u003e\n     spacesvr\n\u003c/h3\u003e\n\u003ch5 align=\"center\"\u003e\n     A standardized reality for the future of the 3D Web.\n\u003c/h5\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Version](https://img.shields.io/npm/v/spacesvr?style=flat\u0026colorA=000000\u0026colorB=000000)](https://www.npmjs.com/package/@react-three/drei)\n[![Downloads](https://img.shields.io/npm/dt/spacesvr.svg?style=flat\u0026colorA=000000\u0026colorB=000000)](https://www.npmjs.com/package/@react-three/drei)\n[![Discord Shield](https://img.shields.io/discord/610733384804859934?style=flat\u0026colorA=000000\u0026colorB=000000\u0026label=discord\u0026logo=discord\u0026logoColor=ffffff)](https://discord.gg/nFHrmUbaz5)\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://muse.place?utm_source=npmjs\"\u003ewww.muse.place\u003c/a\u003e · \u003ca href=\"https://spacesvr.io/\"\u003edemo\u003c/a\u003e · \u003ca href=\"https://discord.gg/nFHrmUbaz5\"\u003ediscord\u003c/a\u003e\n\u003cp\u003e\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://muse.place?utm_source=npmjs\u0026utm_campaign=logo\"\u003e\n    \u003cimg width=\"50\" src=\"https://d27rt3a60hh1lx.cloudfront.net/images/muselogogray.png\" alt=\"logo\" /\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003chr/\u003e\n\n## About\n\nThe mission of spacesvr is to organize and implement the standards for experiencing 3D content on the web in the same way that there exists standards for experiencing 2D content with HTML/CSS/JS.\n\nspacesvr is designed to empower the artist. Instead of worrying about file structures or basic functionality like cross-device compatability, artists should spend their time telling their story. As such, consumption is optimized for simplicity, and the organization provides a framework to tell stories.\n\nspacesvr is actively maintained by [Muse](https://www.muse.place?utm_source=npmjs\u0026utm_campaign=learn_more), a YC-backed startup that provides tooling for visually building worlds. Muse's mission is to accelerate the adoption of 3D websites by increasing their accessibility, both for the end user and for the creator. Muse is completely built on spacesvr.\n\n## Quick Start\n\n- [Visit the codesandbox](https://codesandbox.io/s/e9w29) to instantly play with the package\n- [Clone the starter repo](https://github.com/spacesvr/spacesvr-starter) to start using with Next.js\n- [Sign up for muse](https://www.muse.place?utm_source=npmjs\u0026utm_campaign=no_code) to build using our no-code editor\n\n## Develop\n\nYou could set up the framework in your sleep. Just import the package\n\n```bash\nyarn add spacesvr #or npm install spacesvr\n```\n\nand copy/paste 9 lines of code\n\n```tsx\nimport { StandardReality, LostWorld } from \"spacesvr\";\n\nfunction World() {\n  return (\n    \u003cStandardReality\u003e\n      \u003cLostWorld /\u003e // an example world with a floor, skybox, and fog\n    \u003c/StandardReality\u003e\n  );\n}\n```\n\n_this is the starting point for [this demo](https://spacesvr.io)_\n\n\u003cbr/\u003e\n\nFrom this point, your creations will be built by directly using the following technologies:\n\n- [@react-three/fiber](https://github.com/pmndrs/react-three-fiber), the most important to learn\n- [@react-three/drei](https://github.com/pmndrs/drei), a repository of components and utilities\n- [@react-three/cannon](https://github.com/pmndrs/use-cannon), for physics\n- [@react-spring/three](https://github.com/pmndrs/react-spring#readme), for animations\n\nUnder the hood and mostly abstracted away are the following technologies:\n\n- [three.js](https://github.com/mrdoob/three.js/), an abstract 3D API for the web\n- [@react-three/xr](https://github.com/pmndrs/react-xr), for any VR-specific interactions\n- [peerjs](https://github.com/peers/peerjs), for networking\n\n## Folder Structure\n\n_the following are each a fundamental unit and their own folder at the top level of spacesvr, with the pattern recursively re-appearing throughout the repository_\n\n**ideas** are the fundamental building blocks of your world. They are the 3D the equivalent of HTML Elements. They are implemented as React components.\n\n**layers** (of reality) offer new functionality to the world. They are implemented using a React container component, a context, and a corresponding hook.\n\n**logic** offers functions to be used when composing your world. They are implemented as individually exported functions and hooks.\n\n**realities** define how the player experiences your world. They are comparable in function to a browser. They are implemented as a React container component and composed of an ordering of layers.\n\n**tools** offer the player affordances in your world. They are the 3D equivalent of a browser toolbar. They are implemented using a Layer for fundamental state a modifier for registry.\n\n**worlds** are sets of ideas. They are the actual content of your site. They are implemented as compositions of ideas.\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n\u003chr/\u003e\n\n## Realities\n\n#### Standard Reality\n\nThe Standard Reality defines the standard experiencing the 3D web. The layers provided are, in order: Environment, Physics, Player, Toolbelt, Network, Visual. Additionally, it provides an infinite ground to walk on that can be disabled.\n\n```tsx\n\u003cStandardReality\n    environmentProps={{...}} // props to be passed to the environment layer\n    physicsProps={{...}} // props to be passed to the physics layer\n    playerProps={{...}} // props to be passed to the player layer\n    networkProps={{...}} // props to be passed to network layer\n    disableGround={true} // disable ground in case you want your own\n/\u003e\n```\n\n## Layers\n\n#### Environment Layer\n\n_The base layer that abstracts away the DOM, allowing you to think only in 3D_\n\n```tsx\ntype EnvironmentProps = {\n  name?: string; // set the name of your environment, shows up in the pause menu\n  pauseMenu?: ReactNode; // for you to provide a custom pause menu\n  loadingScreen?: ReactNode; // for you to provide a custom loading screen\n  dev?: boolean; // hides the pause menu, useful while switching between world and IDE\n  canvasProps?: Partial\u003cContainerProps\u003e; // to edit the r3f canvas props\n};\n```\n\n```tsx\nconst environmentState = useEnvironment();\n\ntype EnvironmentState = {\n  name: string;\n  paused: boolean;\n  setPaused: (p: boolean) =\u003e void;\n  device: DeviceState; // the current device being used by the player: xr, mobile, or desktop\n  containerRef: MutableRefObject\u003cHTMLDivElement | null\u003e;\n};\n```\n\n#### Physics Layer\n\n_Provides a default physics configuration_\n\n```tsx\nimport { PhysicsProps as ProviderProps } from \"@react-three/cannon\";\ntype PhysicsProps = ProviderProps;\n```\n\n#### Player Layer\n\n_Provides a user-controlled entity with a standardized set of controls that work cross-platform (VR/Mobile/Desktop)_\n\n```tsx\ntype PlayerProps = {\n  pos?: number[]; // initial player position\n  rot?: number; // initial player rotation (radians along Y axis)\n  speed?: number; // player movement speed\n  controls?: {\n    disableGyro?: boolean; // used to disable gyroscope prompt on mobile\n  };\n};\n```\n\n```tsx\nconst playerState = usePlayer();\n\ntype PlayerState = {\n  position: PlayerVec; //.get() and .set() for position\n  velocity: PlayerVec; //.get() and .set() for velocity\n  controls: PlayerControls; //.lock() and .unlock() for stopping player movement\n  raycaster: Raycaster; // reference to player's raycaster\n};\n```\n\n#### Toolbelt Layer\n\n_Provides a layer of UX to offer user interaction with the world._\n\n```tsx\ntype ToolbeltProps = {\n  showOnSpawn?: boolean; // whether to show the toolbelt on spawn, default true\n};\n```\n\n```tsx\nconst toolbelt = useToolbelt();\n\ntype ToolbeltState = {\n  tools: Tool[];\n  activeTool?: Tool;\n  hide: () =\u003e void;\n  next: () =\u003e void;\n  prev: () =\u003e void;\n  show: () =\u003e void;\n  activeIndex: number | undefined;\n  setActiveIndex: (i: number) =\u003e void;\n  direction: Direction;\n};\n```\n\n#### Network Layer\n\n_Provides multiplayer out-of-the-box. Muse provides signalling servers and [STUN/TURN](https://www.twilio.com/docs/stun-turn/faq#faq-what-is-nat) servers for everyone :)._\n\n```tsx\ntype NetworkProps = {\n  autoconnect?: boolean; // whether to automatically attempt a p2p connection, false by default\n  disableEntities?: boolean; // whether to hide users from seeing each other, in case you want to implement yourself\n  iceServers?: RTCIceServer[]; // set of ice servers to use (recommended for production!!)\n  host?: string; // signalling host url, uses Muse's servers by default\n  sessionId?: string; // if you know the session id you want to use, enter it here\n  worldName?: string; // the worldname to hash your signal peers by, by default set to the path name\n  voice?: boolean; // whether to enable spatial voice chat, false by default\n};\n```\n\n```tsx\nconst networkState = useNetwork();\n\ntype NetworkState = {\n  connected: boolean; // whether the user has established connection with signalling server\n  connect: (config?: ConnectionConfig) =\u003e Promise\u003cvoid\u003e; // when autoconnect is off, use this to manually connect\n  connections: Map\u003cstring, DataConnection\u003e; // reference to active peer connections\n  disconnect: () =\u003e void;\n  voice: boolean; // whether voice is enabled\n  setVoice: (v: boolean) =\u003e void; // enable/disable voice\n  mediaConnections: Map\u003cstring, MediaConnection\u003e; // reference to active media connections\n  useChannel: \u003cData = any, State = any\u003e(\n    id: string,\n    type: ChannelType,\n    reducer: Reducer\u003cData, State\u003e\n  ) =\u003e Channel\u003cData, State\u003e; // set up a data channel to standardize communication between peers\n};\n```\n\n## Tools\n\n#### Camera Tool\n\nA tool that gives the user a camera to take pictures with. To add to your toolbelt simply add it into the World.\n\n```tsx\n\u003cStandardReality\u003e\n  \u003cCamera /\u003e\n\u003c/StandardReality\u003e\n```\n\n#### Walkie Talkie Tool\n\nA tool to configure your microphone settings. Automatically added if voice chat is enabled in the network layer.\n\n## Ideas\n\n### _types of ideas_\n\n- [basis/](#basis) for visualizations of fundamental metaphysics\n- [environment/](#environment) for setting up the environment\n- [media/](#media) for importing common media types\n- [mediated/](#mediated) for some basic art assets\n- [modifiers/](#modifiers) for modifying other ideas. they don't render anything themselves\n- [ui/](#ui) for guiding and interacting with the user\n\n---\n\n### basis/\n\n#### VisualIdea\n\nVisualize an Idea\n\n```tsx\n\u003cVisualIdea idea={new Idea()} /\u003e\n```\n\n#### VisualSite\n\nVisualize a Site\n\n```tsx\n\u003cVisualSite idea={new Site()} /\u003e\n```\n\n#### VisualWorld\n\nVisualize a World\n\n```tsx\n\u003cVisualWorld idea={new World()} /\u003e\n```\n\n---\n\n### environment/\n\n#### Background\n\nSet the background color of your space\n\n```tsx\n\u003cBackground color=\"blue\" /\u003e\n```\n\n#### Fog\n\nAdd fog to your scene.\n\n```tsx\n\u003cFog color=\"white\" near={10} far={100} /\u003e\n```\n\n#### InfinitePlane\n\nAdds an infinite plane to walk on (added by default with the Environment Layer)\n\n```tsx\n\u003cInfinitePlane\n  height={-0.0001} // offset a slight amount\n  size={[100, 100]}\n  visible={false}\n/\u003e\n```\n\n---\n\n### media/\n\n#### Audio\n\nA positional audio component that will play the passed in audio url. Handles media playback rules for Safari, iOS, etc.\n\n```tsx\n\u003cAudio\n  url=\"https://link-to-your-audio.mp3\"\n  position={[0, 4, 0]}\n  volume={1}\n  rollOff={1}\n  dCone={new Vector3(coneInnerAngle, coneOuterAngle, coneOuterGain)} // defaults should be fine\n  fftSize={128}\n/\u003e\n```\n\n#### HDRI\n\nSet the scene background to an hdr file. You can find free hdr files here: https://hdrihaven.com/\n\n```tsx\n\u003cHDRI\n  src=\"https://link-to-your-hdri.hdr\"\n  disableBackground={false} // used to disable visual hdr (skybox)\n  disableEnvironment={false} // used to disable environment map\n/\u003e\n```\n\n#### Image\n\nQuickly add an image to your scene\n\n```tsx\n\u003cImage\n  src=\"https://link-to-your-image.png\"\n  size={1} // size, default normalized to longest side = 1\n  framed // adds a frame\n/\u003e\n```\n\n#### Model\n\nQuickly add a GLTF/GLB model to your scene. Will handle Suspense, KTX2, Draco, Meshopt. Clones the gltf scene so the same file can be re-used.\n\n```tsx\n\u003cModel\n  src=\"https://link-to-your-model.glb\"\n  center // whether to center the model so its bounds are centered on its origin, default false\n  normalize // whether to normalize the model to a height/width/depth of 1, default false\n/\u003e\n```\n\n#### Video\n\nAdd a video file to your space with positional audio. Handles media playback rules for Safari, iOS, etc.\n\n```tsx\n\u003cVideo\n  src=\"https://link-to-your-video.mp4\"\n  size={1} // size, default normalized to longest side = 1\n  volume={1}\n  muted // mutes the video\n  framed // adds a frame\n/\u003e\n```\n\n---\n\n### mediated/\n\n#### Frame\n\nBuilds a frame to showcase media, especially images.\n\nwidth: number;\nheight: number;\nthickness?: number;\nmaterial?: Material;\ninnerFrameMaterial?: Material;\n\n```tsx\n\u003cFrame\n  width={1}\n  height={1}\n  thickness={0.1} // optional, default 0.1\n  material={new MeshBasicMaterial({ color: \"red\" })} // optional, default is a black MeshStandardMaterial\n  innerFrameMaterial={new MeshBasicMaterial({ color: \"blue\" })} // optional, default is no inner frame\n/\u003e\n```\n\n#### LostFloor\n\nAn infinite floor styled to the Lost World.\n\n```tsx\n\u003cLostFloor /\u003e\n```\n\n---\n\n### modifiers/\n\n#### Collidable\n\nEnables colliders for its children either by a named collider mesh or using all meshes and capping collective triangle count to triLimit prop.\n\n```tsx\n\u003cCollidable\n  triLimit={1000} // max number of triangles before it uses bvh\n  enabled={true}\n  hideCollisionMeshes={false} // set visible to false on meshes used for collision\n/\u003e\n```\n\n#### Interactable\n\nMakes its children react to onclick and on hover methods\n\n```tsx\n\u003cInteractable\n  onClick={() =\u003e console.log(\"Ive been clicked!\")}\n  onHovered={() =\u003e console.log(\"Ive been hovered!\")}\n  onUnHovered={() =\u003e console.log(\"Ive been unhovered?\")}\n\u003e\n  \u003cStuff /\u003e\n\u003c/Interactable\u003e\n```\n\n#### Tool\n\nTurns its children into a tool, automatically registers it with the Tool Layer.\n\n```tsx\n\u003cTool\n  name=\"My Tool\" // name used for identification\n  pos={[0, 0]} // where the tool should be positioned in screen space, x:[-1, 1], y:[-1, 1]\n  face={true} // whether the tool should face the user, default true\n  pinY={false} // whether the tool should be pinned on the screen space y so user can look up and down\n  range={0} // how far from the cursor the tool will stay without moving left or ight, along the x screen space axis, measured in degrees\n  orderIndex={0} // the order in which the tool should be rendered relative to other orders, default 0, sorts low to high\n  onSwitch={(enabled: boolean) =\u003e {}} // callback for when active tool switches, passes whether the given tool is enabled\n\u003e\n  \u003cStuff /\u003e\n\u003c/Tool\u003e\n```\n\n#### Anchor\n\nMakes its children link out to when clicked. handles leaving vr session.\n\n```tsx\n\u003cAnchor\n  href=\"https://link-to-your-website.com\"\n  target=\"_blank\" // optional, default is _self\n\u003e\n  \u003cStuff /\u003e\n\u003c/Anchor\u003e\n```\n\n#### FacePlayer\n\nTurns its children into a billboard, always facing the camera.\n\n```tsx\n\u003cFacePlayer enabled={true} lockX={false} lockY={false} lockZ={false} /\u003e\n```\n\n#### Floating\n\nLazily floats its children.\n\n```tsx\n\u003cFloating height={0.2} speed={1} /\u003e\n```\n\n#### LookAtPlayer\n\nMakes its children face the player, but with easing.\n\n```tsx\n\u003cLookAtPlayer enabled={true} /\u003e\n```\n\n#### Spinning\n\nMakes its children spin\n\n```tsx\n\u003cSpinning xSpeed={0} ySpeed={1} zSpeed={0} /\u003e\n```\n\n#### Visual Effect\n\nAdds a render pass to the Visual Layer's render pipeline. Use to add postprocessing.\n\n```tsx\n\u003cVisualEffect\n  index={1} // the order in which the effects are applied, sorts low to high\n\u003e\n  \u003cunrealBloomPass args={[new Vector2(256, 256), 0.1, 0.01, 0.95]} /\u003e\n\u003c/VisualEffect\u003e\n```\n\n---\n\n### ui/\n\n#### TextInput\n\nA text input component made to mimic an HTML input element. Supports all shortcuts, drag to select, shift click, double/triple click.\n\n```tsx\nconst [text, setText] = useState(\"\");\n\n\u003cTextInput\n  type=\"text\" // text | password | number, default is text\n  value={text} // control the input value\n  onChange={setText} // optional onChange function\n  onSubmit={(s: string) =\u003e console.log(s)} // optional onSubmit function, called when enter is pressed\n  onFocus={() =\u003e console.log(\"focused\")} // optional onFocus function\n  onBlur={() =\u003e console.log(\"blurred\")} // optional onBlur function\n  font={\"https://link-to-your-font.ttf\"} // optional font\n  fontSize={0.1} // font size, default 0.1\n  width={1} // width, default 1\n  placeholder=\"Enter your name\" // optional placeholder text\n/\u003e;\n```\n\n#### Arrow\n\nAn arrow icon\n\n```tsx\n\u003cArrow dark={false} /\u003e\n```\n\n#### Button\n\nA simple button\n\n```tsx\n\u003cButton\n  onClick={() =\u003e console.log(\"Ive been clicked!\")}\n  font=\"https://link-to-your-font.ttf\" // optional font, default is Quicksand\n  fontSize={0.1} // font size, default 0.05\n  maxWidth={1} // max width, default no max width\n  textColor=\"red\" // text color, default black\n  color=\"green\" // button color, default white\n  outline={false} // whether to show an outline, default true\n  outlineColor=\"#9f9f9f\" // outline color, default white\n\u003e\n  Click me!\n\u003c/Button\u003e\n```\n\n#### Key\n\nA Keyboard Key that responds to the corresponding key press. Useful for tutorials.\n\n```tsx\n\u003cKey\n  keyCode=\"a\"\n  keyPress={[\"a, A\"]} // optional, default is keyCode, but in case there are multiple keys to look for\n  onPressed={(evt) =\u003e console.log(\"Ive been pressed!\")} // optional callback when key is pressed\n/\u003e\n```\n\n#### Switch\n\nA boolean switch\n\n```tsx\nconst [value, setValue] = useState(false);\n\n\u003cSwitch\n  value={value} // control the switch value\n  onChange={setValue} // optional onChange function\n/\u003e;\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmusehq%2Fspacesvr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmusehq%2Fspacesvr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmusehq%2Fspacesvr/lists"}