Multiplayer in Godot is WebRTC under the hood. Add TURN, players connect.
Godot's WebRTC API is in the official webrtc module (compiled into the engine in 4.x).
extends Node
var peer := WebRTCPeerConnection.new()
func _ready() -> void:
peer.initialize({
"iceServers": [
{ "urls": ["stun:stun.expressturn.com:3478"] },
{
"urls": [
"turn:relay1.expressturn.com:3478?transport=udp",
"turn:relay1.expressturn.com:3478?transport=tcp",
"turns:relay1.expressturn.com:443?transport=tcp"
],
"username": "YOUR_EXPRESSTURN_USERNAME",
"credential": "YOUR_EXPRESSTURN_PASSWORD"
}
]
})
var ch := peer.create_data_channel("game", { "id": 1, "negotiated": true })
peer.session_description_created.connect(_on_offer)
peer.create_offer()
func _on_offer(type: String, sdp: String) -> void:
peer.set_local_description(type, sdp)
# send sdp to your signaling server
var multiplayer_peer := WebRTCMultiplayerPeer.new()
multiplayer_peer.create_mesh(1) # 1 = host
multiplayer.multiplayer_peer = multiplayer_peer
# When a player joins, create a connection for them and add it
func add_player(peer_id: int) -> void:
var conn := WebRTCPeerConnection.new()
conn.initialize({ "iceServers": ICE_SERVERS }) # same array as above
multiplayer_peer.add_peer(conn, peer_id)
# exchange offer/answer/ICE candidates with that peer's signaling channel
Godot games exported to HTML5 use the browser's native WebRTC. Same iceServers configuration applies — Godot translates the dictionary into RTCConfiguration. The TURN-over-TLS path on port 443 is what saves your players on locked-down networks.
module_webrtc_enabled=yes.peer.poll(): WebRTCPeerConnection in Godot needs poll() called every frame. Easy to miss; connection appears stuck.Related: TURN for multiplayer games · Pion (Go server) recipe