Serverless P2P Multiplayer in Godot — No Backend, Just GenosDB
How I turned a single-player Godot game into real-time co-op with zero servers — two browser tabs, one shared world, powered by GenosDB.

Full Stack Developer - dWEB R&D
Try it first: open the live demo in two browser tabs → https://estebanrfp.github.io/godot-genosdb/ Each tab is a different player. Walk around, chop a tree — it falls in every window. No server is running. Anywhere.
The challenge
Multiplayer usually means servers: a backend, sockets, authoritative state, deployment, scaling, cost. For a small game — or just a prototype — that's a lot of plumbing before the fun part.
What if the players' browsers just talked directly to each other, and the shared world simply… persisted, on its own?
That's what this demo does: a top-down, Stardew-style co-op farm built in Godot 4, exported to the Web, where every connected browser is another farmer in the same world — running on no backend at all. The secret is GenosDB.
What is GenosDB?
GenosDB is a decentralized graph database that runs entirely in the browser. Two parts matter here:
- GenosRTC — peer-to-peer transport over WebRTC, with decentralized signaling via Nostr relays (no signaling server you run). Peers find each other and open direct data channels.
- A reactive, persistent graph — you
putnodes andmapqueries, and changes sync to every peer and persist (OPFS). New peers automatically receive the current state.
No backend. No database server. No signaling server. It's all client-side.
The key idea: a hybrid model
Not all multiplayer state is equal, and GenosDB lets you treat it accordingly. The demo splits the world into two layers — and this is the pattern worth stealing:
| State | Example | How | Why |
|---|---|---|---|
| Ephemeral | Player positions | a data channel (channel.send) |
fast, ~16 Hz, throwaway |
| Persistent | Which trees are chopped | the graph (db.put / db.map) |
synced and stored; late-joiners see the world as it is |
That second row is the magic. When you chop a tree you don't shout "I chopped
tree 3!" (only people currently listening hear it). You write it into a shared
graph — and anyone who opens a new tab later gets the already-chopped world via
GenosDB's initial event. Chops survive reloads. That's a database, working P2P,
for free.
Connecting Godot to GenosDB
GenosDB is browser-only, and Godot reaches the browser through its Web (HTML5)
export + JavaScriptBridge. So I built a small bridge and packaged it as a
plugin — godot-genosdb — that
auto-injects the JS bridge into your exported index.html. Enable it, export
to Web, done. Nothing to bundle, no HTML to edit.
The API mirrors GenosDB
On purpose: learn the plugin and you've learned GenosDB.
func _ready():
Net.peer_join.connect(func(id): spawn_farmer(id))
Net.message.connect(_on_remote_state) # ephemeral
Net.graph_changed.connect(_on_world) # persistent
Net.join("my-room") # = gdb("my-room", {rtc:true})
Net.map({}) # = db.map({}, cb)
func _physics_process(_d):
Net.send({ "x": position.x, "y": position.y }) # = channel.send(...)
func chop_tree():
Net.put({ "type": "tree", "hp": 0 }, "tree_3") # = db.put(node, id)
| GenosDB (JS) | Godot (Net) |
|---|---|
gdb(name, {rtc:true}) |
Net.join(name) |
channel.send(data) |
Net.send(data) → signal message |
db.put(node, id) |
Net.put(data, id) → signal graph_changed |
db.map(query, cb) |
Net.map(query) |
room.on('peer:join' / 'leave') |
signals peer_join / peer_leave |
Try it / use it
- ▶ Live demo (two tabs!): https://estebanrfp.github.io/godot-genosdb/
- Plugin + full source (MIT): https://github.com/estebanrfp/godot-genosdb
- GenosDB: https://github.com/estebanrfp/GenosDB
Install is three steps: copy addons/godot_genosdb/, enable it in Project
Settings → Plugins, export to Web. P2P is Web-only; on desktop the API is a safe
no-op, so the same code runs as single-player in the editor.
Why this matters
For jams, prototypes, collaborative toys, or "presence" features, serverless P2P removes an entire category of work. And because GenosDB gives you a persistent reactive graph — not just a raw WebRTC pipe — your shared world is consistent, survives reconnects, and welcomes late-joiners with zero extra code.
Two browser tabs. One shared world. No backend. Just GenosDB.




