2026-07-31 19:46:50 +02:00
|
|
|
|
XRForge is a tiny, powerful tool to interconnect XR experiences.<br>
|
2026-08-03 19:19:07 +02:00
|
|
|
|
<br>
|
|
|
|
|
|
An open, seamless XR web built from simple URLs and directories, without gatekeepers.<br>
|
|
|
|
|
|
<br>
|
|
|
|
|
|
<br>
|
|
|
|
|
|
XRForge transforms standard local or remote file directories into interconnected 3D spaces.
|
|
|
|
|
|
<br>Users can seamlessly "surf" the spatial web, turning everyday directories into 3D portals.
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌───────────────────────────────────┐ ┌──────────────────────────────────────────┐
|
|
|
|
|
|
│ local disk │ │ Surf via 3D portals │
|
|
|
|
|
|
├───────────────────────────────────┤ └──────────────────────────────────────────┘
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ /myverse │ https://my.org/myverse
|
|
|
|
|
|
│ /elearnings │ https://my.org/myverse/elearnings
|
|
|
|
|
|
│ /elearning1/1.glb │ https://my.org/myverse/elearning/elearning1
|
|
|
|
|
|
│ /elearning2/2.glb │ https://my.org/myverse/elearning/elearning2
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ ┌──────────────────────────────┐ │
|
|
|
|
|
|
│ │ github / webdav / dropbox etc│ │
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ │ /mygames │ │ https://my.org/myverse/mygames
|
|
|
|
|
|
│ │ /biggusgateWebXR │ │ https://my.org/myverse/mygames/biggusgate
|
|
|
|
|
|
│ │ /spasca │ │ https://my.org/myverse/mygames/spasca
|
|
|
|
|
|
│ │ /otherxrforge │ │ https://my.org/myverse/otherxrforge
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ │ ┌───────────┐ ┌───────────┐ │ │
|
|
|
|
|
|
│ │ │/manyfold │ │archive.org│ │ │ https://my.org/myverse/manyfold
|
|
|
|
|
|
│ │ └───────────┘ └───────────┘ │ │ https://my.org/myverse/myarchive
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ │ +40 integrations! │ │
|
|
|
|
|
|
│ └──────────────────────────────┘ │
|
|
|
|
|
|
└───────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Usage / Radical Opensource
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ ./xrforge.com
|
|
|
|
|
|
Usage: ./xrforge.com <cmd> [opts]
|
|
|
|
|
|
|
|
|
|
|
|
./xrforge.com test run testsuite (selftest)
|
|
|
|
|
|
./xrforge.com admin launch adminpanel to configure stuff (/admin)
|
|
|
|
|
|
./xrforge.com generate generate www-folder with XR experiences
|
|
|
|
|
|
./xrforge.com help display all possible flags
|
|
|
|
|
|
./xrforge.com createplugin generates skeleton plugin
|
|
|
|
|
|
./xrforge.com server starts the webserver [without --nobrowser]
|
|
|
|
|
|
./xrforge.com build build
|
|
|
|
|
|
|
|
|
|
|
|
$ unzip xrforge.com
|
|
|
|
|
|
Archive: result/bin/xrforge.com
|
|
|
|
|
|
Length Date Time Name
|
|
|
|
|
|
--------- ---------- ----- ----
|
|
|
|
|
|
3992 08-03-2026 09:54 .init.lua
|
|
|
|
|
|
242 08-03-2026 09:46 cmd/server.lua
|
|
|
|
|
|
1014 07-23-2026 16:13 cmd/createplugin.lua
|
|
|
|
|
|
580 07-24-2026 16:12 cmd/help.lua
|
|
|
|
|
|
....
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> Profit! you have aqcuired the sourcecode from the binary: run and distribute modified versions if needed ♥
|
2026-05-15 23:57:23 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
## Adding content
|
2026-05-15 23:57:23 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
First you need a directory with glTF or JanusXR experiences.<br>
|
|
|
|
|
|
Just run this in the terminal:
|
2026-05-15 23:57:23 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
```bash
|
|
|
|
|
|
$ unzip xrforge.com myverse
|
|
|
|
|
|
$ tree myverse
|
|
|
|
|
|
myverse
|
|
|
|
|
|
├── janusxr
|
|
|
|
|
|
│ ├── helloworld
|
|
|
|
|
|
│ │ └── janusxr.xml
|
|
|
|
|
|
│ ├── janusxr.png
|
|
|
|
|
|
│ └── tronverse -> ../tronverse <---
|
|
|
|
|
|
└── tronverse | recursive portals allowed
|
|
|
|
|
|
├── janusxr -> ../janusxr <--- via symlinks
|
|
|
|
|
|
├── tron
|
|
|
|
|
|
│ ├── README.md
|
|
|
|
|
|
│ ├── tron.glb
|
|
|
|
|
|
│ ├── tron.mp3
|
|
|
|
|
|
│ └── tron.png
|
|
|
|
|
|
└── tronverse.png
|
|
|
|
|
|
```
|
2026-07-23 10:01:34 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
> optionally you can run `./xrforge.com admin` and change the `myverse` directory if needed.
|
2026-07-23 10:01:34 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
Now xrforge.com will scan that local folder:
|
2026-05-15 23:57:23 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
```bash
|
|
|
|
|
|
$ ./xrforge.gom
|
|
|
|
|
|
scanning experiences (this make take a while..)
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected .png thumbnail file
|
|
|
|
|
|
✅ ./myverse/janusxr 10927498
|
|
|
|
|
|
✅ ./myverse/janusxr/helloworld 10927500
|
|
|
|
|
|
✅ └─ plugin/janusxr: janusxr.xml
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected .png thumbnail file
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected .png thumbnail file
|
|
|
|
|
|
✅ ./myverse/tronverse 10927503
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected .png thumbnail file
|
|
|
|
|
|
✅ ./myverse/tronverse/tron 10927504
|
|
|
|
|
|
✅ └─ plugin/xrfragments: detected .png sidecarfile
|
|
|
|
|
|
✅ └─ plugin/markdown: detected README.md
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected .png thumbnail file
|
|
|
|
|
|
ℹ️ ./myverse/janusxr/tronverse
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected symlink..creating portal
|
|
|
|
|
|
ℹ️ ./myverse/tronverse/janusxr
|
|
|
|
|
|
✅ └─ plugin/diskscan: detected symlink..creating portal
|
2026-05-15 23:57:23 +02:00
|
|
|
|
```
|
2026-07-21 19:19:52 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
## Customizing
|
2026-07-23 10:01:34 +02:00
|
|
|
|
|
2026-07-31 19:46:50 +02:00
|
|
|
|
After first (in the terminal) run a lua config file is created, which you can edit:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ xrforge.com
|
|
|
|
|
|
$ ls
|
|
|
|
|
|
xrforge.com
|
|
|
|
|
|
xrforge.lua
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
With basic HTML/CSS skills you can edit the templates by running this in the terminal:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ unzip xrforge.com page css
|
|
|
|
|
|
$ ls
|
|
|
|
|
|
page css
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> Profit! You can now edit the extracted html-templates and css-files, they will be detected automatically when running xrforge.com
|
|
|
|
|
|
|
|
|
|
|
|
Optionally you can re-distribute `xrforge.com` with your modifications by running `zip xrforge.com page`
|
|
|
|
|
|
|
|
|
|
|
|
## Plugins
|
|
|
|
|
|
|
|
|
|
|
|
xrforge is basically plugins working together.<br>
|
|
|
|
|
|
You can create your own plugins by running:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ ./xrforge.com createplugin > xrforge.myplugin.lua
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Look in the [src/plugin](src/plugin) directory for examples
|
2026-07-23 10:01:34 +02:00
|
|
|
|
|
2026-08-06 17:09:24 +02:00
|
|
|
|
## Run as server
|
|
|
|
|
|
|
|
|
|
|
|
<b>BAREMETAL:</b>
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ ./xrforge.com server
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> **NOTE**: if the binary does not work on your server, use [xrforge-x86_64]() from the releases section instead.
|
|
|
|
|
|
|
|
|
|
|
|
<b>DOCKER:</b> to prevent polluting the environment with terabytes of docker-images, just run this:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ docker run -d -p 8080:8080 -v $(pwd):/app busybox /app/xrforge.com server
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-21 19:19:52 +02:00
|
|
|
|
## Scan manyfold
|
|
|
|
|
|
|
|
|
|
|
|
-------------
|
|
|
|
|
|
```
|
|
|
|
|
|
manual scan (via cli?)
|
|
|
|
|
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H 'accept: application/vnd.manyfold.v0+json'
|
|
|
|
|
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json'
|
|
|
|
|
|
```
|
2026-07-22 18:36:02 +02:00
|
|
|
|
|
2026-07-23 15:20:25 +02:00
|
|
|
|
## Developing
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ source .env # not needed if your system has autoenv
|
|
|
|
|
|
$ build # fast: repeatable build
|
|
|
|
|
|
$ rebuild # faster: lua-repeatable build
|
|
|
|
|
|
$ nix-build # slow: reproducable nix build (for experts)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Then run with debug flag to see output of `trace()` calls
|
|
|
|
|
|
```bash
|
|
|
|
|
|
$ DEBUG=1 result/bin/xrforge.com
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-08-06 16:43:17 +02:00
|
|
|
|
> Testing: run the builtin testrunner: `cd src && ../result/bin/xrforge.com test`
|
|
|
|
|
|
|
|
|
|
|
|
Use `ARCH=x86_64 build` to enforce x86_64 output (instead of a cosmopolitan binary)
|
|
|
|
|
|
|
2026-07-22 18:36:02 +02:00
|
|
|
|
## Permacomputing Spirit
|
|
|
|
|
|
|
|
|
|
|
|
* developed on [Dillo](https://dillo-browser.github.io/) as lowest common denominator / compatibility
|
2026-07-23 10:01:34 +02:00
|
|
|
|
* does not use javascript (WebXR viewer being the exception)
|
|
|
|
|
|
* gracefully degrades to admin/filebrowser for >noscript>/potato-browsers
|
2026-07-22 18:36:02 +02:00
|
|
|
|
* server is using lua-scripting via cosmopolitan libc's [redbean](https://redbean.dev)
|
|
|
|
|
|
|
2026-07-23 10:01:34 +02:00
|
|
|
|
> So yes, the codebase/html/css is intentionally 90-ish: that's **exactly why it works** on potato-devices AND modern devices.
|
2026-07-30 20:53:12 +02:00
|
|
|
|
|
|
|
|
|
|
## Credits / Made with
|
|
|
|
|
|
|
|
|
|
|
|
* [redbean](https://redbean.dev)
|
|
|
|
|
|
* [soakbean](https://github.com/coderofsalvation/soakbean)
|
|
|
|
|
|
* [xrfragments](https://xrfragment.org)
|
|
|
|
|
|
* [janusxr](https://github.com/jbaicoianu/janusweb)
|
|
|
|
|
|
* [janusweb](https://janusxr.org)
|
2026-07-31 19:46:50 +02:00
|
|
|
|
|
|
|
|
|
|
## Ideas
|
|
|
|
|
|
|
|
|
|
|
|
* tabs horizontal stacked for xrforge.max.css
|
|
|
|
|
|
* fuzzy search: https://github.com/ssimonp/FuzzySearch-Roblox
|
|
|
|
|
|
* landingpage = /about
|
|
|
|
|
|
* scan functions in xrforge.lua / .xrforge
|
|
|
|
|
|
* video-hover: https://codepen.io/coderofsalvation/pen/YPNOyqx
|
|
|
|
|
|
|
|
|
|
|
|
NOTES:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
/admin.lua <--- define other instances..for CORS allow / mount http
|
|
|
|
|
|
configure in-zip or filesystem-cache
|
|
|
|
|
|
|
|
|
|
|
|
opendirectory.lua <--- mount other instances over http (via xrforge or rclone)
|
|
|
|
|
|
|
|
|
|
|
|
.xrforge/hook/scandir <-- event to hook
|
|
|
|
|
|
|
|
|
|
|
|
scan files
|
|
|
|
|
|
---------- middleware/xrexperience.lua
|
|
|
|
|
|
| events: ---------------------
|
|
|
|
|
|
+ file type: sidecarfile
|
|
|
|
|
|
+ sidecarfiles ---> regex = {"\.glb", "\.png", "\.vtt"}
|
|
|
|
|
|
| regex_minmatch = 2
|
|
|
|
|
|
+ folder viewer = /view/janusweb/#janus.url=${file}.glb
|
|
|
|
|
|
template = [[
|
|
|
|
|
|
<div class="tile"> ... </div>
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|