Skip to main content

Command Palette

Search for a command to run...

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.

Updated
4 min read
Serverless P2P Multiplayer in Godot — No Backend, Just GenosDB
E

Full Stack Developer - dWEB R&D

Try it first: open the live demo in two browser tabshttps://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 put nodes and map queries, 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

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.