The Wavedash SDK gives your game access to player accounts, leaderboards, achievements, multiplayer, cloud saves, and more. This page walks you through getting it set up.
How the SDK works
When your game runs on Wavedash, the platform makes Wavedash available in the browser before your code starts. During local development, wavedash dev does the same thing against your game's sandbox data, so you can test without touching production (a network connection and a signed-in CLI are still required).
Methods that talk to servers are asynchronous. In JavaScript they return Promises; in the native bindings they resolve via await, coroutines, or engine-specific response events. Methods that read local state (like the current player) are synchronous.
Typed access in JavaScript/TypeScript
Optionally install @wvdsh/sdk-js and import Wavedash directly in your game script for editor autocomplete and type safety:
npm install @wvdsh/sdk-jsimport Wavedash from "@wvdsh/sdk-js";
Wavedash.init({ debug: true });
Wavedash.on(Wavedash.Events.LOBBY_JOINED, (payload) => {
console.log('Joined lobby', payload.lobbyId);
});The import gives you the host's injected SDK instance with full TypeScript types — methods, events, and payloads all autocomplete. Importing from anywhere in your codebase returns the same SDK singleton.
Initialization
Call Wavedash.init(config?) once, when your game is ready to show, to apply your settings and mark the game as loaded. This is what reveals your game from behind the loading screen, so it's required for every game, even one that uses no other SDK features. init() is synchronous and returns a boolean (true on first call, false if already initialized). You do not need to await it. Other calls work before it — load-progress calls usually come first — but readyForEvents() and the getP2P* getters (max payload size, max incoming messages, outgoing buffer) throw until init() runs, and no events reach your handlers before then. (Godot and Unity take a required config dictionary — pass {} for defaults — and return nothing. Defold takes a config table plus an event callback and returns a boolean.)
init() also calls loadComplete() internally, so a single init() is enough for most games. See Load lifecycle below if you want to report progress and signal load completion separately.
func _ready():
WavedashSDK.backend_connected.connect(_on_connected)
WavedashSDK.init({"debug": true, "deferEvents": true})
WavedashSDK.ready_for_events()
func _on_connected(payload):
print("SDK is ready!")
var username = WavedashSDK.get_username()
print("Playing as: ", username)using System.Collections.Generic;
void Awake()
{
Wavedash.SDK.Init(new Dictionary<string, object>
{
{ "debug", true },
{ "deferEvents", true }
});
Wavedash.SDK.ReadyForEvents();
}
void Start()
{
var user = Wavedash.SDK.GetUser();
if (user != null)
Debug.Log("Playing as: " + user["username"]);
}function init(self)
wavedash.init({ debug = true, deferEvents = true }, function(_, event, payload)
if event == wavedash.EVENT_BACKEND_CONNECTED then
print("SDK is ready!")
local user = wavedash.get_user()
if user then
print("Playing as:", user.username)
end
end
end)
wavedash.ready_for_events()
endWavedash.init({ debug: true, deferEvents: true });
Wavedash.readyForEvents();
const user = Wavedash.getUser();
console.log(`Playing as: ${user.username}`);The Defold binding takes the same config table as JavaScript: wavedash.init({ debug = true, deferEvents = true, p2p = { ... } }, callback). The table is required — pass {} for defaults. The current binding registers its event listeners only after init() runs, so events that fired earlier (an invite, an entitlement grant) are delivered only if you pass deferEvents = true and call wavedash.ready_for_events() once your handler is ready.
Load lifecycle
In Godot and Unity, Wavedash's own engine entrypoint (your exported index.html never runs on Wavedash) drives the loading bar and calls loadComplete() when the engine instance is created. Those bindings don't expose the methods below; still call init() to opt in to platform features. Defold's extension reports engine load progress automatically. Manual load reporting applies to JavaScript, and to any extra loading a Defold game does after the engine starts.
Wavedash tracks two separate signals while your game boots:
- Load progress — a 0-to-1 value the shell uses to render its loading bar. Call
updateLoadProgressZeroToOne(progress)while your game is downloading assets or initializing. - Load complete — fires once, when the player can actually interact.
init()calls this for you, or you can callloadComplete()directly if you want to signal completion before you're ready to initialize the full SDK (e.g., hand-off from a splash screen).
// Report progress while assets download
Wavedash.updateLoadProgressZeroToOne(0.2);
await downloadAssets();
Wavedash.updateLoadProgressZeroToOne(0.8);
await startRuntime();
// Either of these works — init() calls loadComplete() internally
Wavedash.init({ debug: false });
// Wavedash.loadComplete(); // only needed if you call it before init()-- Report progress while assets download
wavedash.update_load_progress_zero_to_one(0.2)
-- download assets...
wavedash.update_load_progress_zero_to_one(0.8)
-- init() marks the game as loaded; call load_complete() only if you
-- need to signal completion before init()
wavedash.init({}, function() end)
-- wavedash.load_complete()loadComplete() is idempotent — calling it again is a no-op. updateLoadProgressZeroToOne() can be called as often as you like.
Loading third-party scripts
Wavedash.loadScript(src) is a small helper that appends a <script> tag to the page and resolves once it finishes loading. Useful for lazy-loading analytics, engine runtimes, or any script you don't want to block initial page load.
loadScript requests the script with crossorigin="anonymous", so the host serving it must send Access-Control-Allow-Origin (and Cross-Origin-Resource-Policy: cross-origin), or the browser blocks it. See Cross-origin isolation.
await Wavedash.loadScript("https://cdn.example.com/analytics.js");
// analytics global is now availableOverlay
toggleOverlay() is deprecated and does nothing. The in-game overlay it used to control no longer exists. The call is kept so older builds don't break; it logs a warning and returns. The Godot, Unity, and Defold wrappers forward to the same no-op. The same goes for isMuted(), requestMute(), and toggleMute().
Response format
Most async SDK methods that talk to Wavedash services return a response object. Check success before using data.
const response = await Wavedash.getLeaderboard("high-scores");
if (response.success) {
console.log(`Leaderboard: ${response.data.name}`);
} else {
console.error(`Error: ${response.message}`);
}-- Defold can work with async SDK methods in two ways:
-- 1) Using a coroutine. Calling an async function from within a coroutine will
-- provide the SDK method response as the return value of the function call.
local co = coroutine.create(function()
local response = wavedash.get_leaderboard_async("high-scores")
if response.success then
print("Leaderboard:", response.data.name)
else
print("Error:", response.message)
end
end)
coroutine.resume(co)
-- 2) Using an event listener. Calling an async function from the main thread
-- will provide the SDK method response as an event to the event listener set
-- when initialising Wavedash.
function init()
wavedash.init({}, function(_, event, payload)
if event == "getLeaderboard" then
if payload.success then
print("Leaderboard:", payload.data.name)
else
print("Error:", payload.message)
end
end
end)
wavedash.get_leaderboard_async("high-scores")
endWhen a call fails, message explains what went wrong.
Error handling
Wrap async service calls in try/catch and check success:
func _on_leaderboard(response):
if not response.get("success", false):
push_error(response.get("message", "leaderboard error"))
return
var board = response["data"]using System;
try
{
var board = await Wavedash.SDK.GetLeaderboard("high-scores");
if (board == null)
{
// null only outside a WebGL build; failed calls throw
Debug.LogWarning("Wavedash SDK is only available in WebGL builds");
return;
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}async function loadBoard() {
try {
const response = await Wavedash.getLeaderboard("high-scores");
if (!response.success) {
console.error(response.message);
return;
}
useBoard(response.data);
} catch (e) {
console.error(e);
}
}Configuration options
Pass these to init() when you need non-default behavior:
| Option | What it does |
|---|---|
debug | Turns on verbose logging in the browser console |
deferEvents | Queues all SDK events (lobby, P2P, backend, stats, fullscreen, entitlements) until you call readyForEvents() |
p2p | Custom P2P networking settings (see Types reference) |
If you set deferEvents: true, make sure you call readyForEvents() once your game is done loading. Otherwise queued events will never reach your handlers.
Launch params
When a player opens your game through a URL with wvdsh_-prefixed query parameters, Wavedash strips the prefix and passes them to your game as key-value pairs. Call getLaunchParams() to read them — it works before init() too.
The most common use case is lobby invite links — when a player clicks an invite link, the lobby param contains the lobby ID so your game can join automatically.
WavedashSDK.init({})
var params = WavedashSDK.get_launch_params()
if params.has("lobby"):
WavedashSDK.join_lobby(params["lobby"])Wavedash.SDK.Init(new Dictionary<string, object>());
var parameters = Wavedash.SDK.GetLaunchParams();
if (parameters.TryGetValue("lobby", out var lobbyId))
await Wavedash.SDK.JoinLobby(lobbyId);function init(self)
wavedash.init({}, function() end)
local params = wavedash.get_launch_params()
if params and params.lobby then
wavedash.join_lobby_async(params.lobby)
end
endWavedash.init();
const params = Wavedash.getLaunchParams();
if (params.lobby) {
await Wavedash.joinLobby(params.lobby);
}How it works
- A player visits a URL like
https://wavedash.com/games/my-game?wvdsh_lobby=j570h2k8p3m7n5q1r4s9t6v2w8x0y3z4 - Wavedash strips the
wvdsh_prefix from matching query parameters - The resulting key-value pairs (
{ lobby: "j570h2k8p3m7n5q1r4s9t6v2w8x0y3z4" }) are passed to your game - Your game reads them with
getLaunchParams()and acts on them
Only query parameters prefixed with wvdsh_ are forwarded to your game. Other query parameters are ignored.
Built-in params
| Param | Description |
|---|---|
lobby | Lobby ID — the player followed an invite link. Join with joinLobby(). See Multiplayer lobbies. |
You can also pass custom data by adding wvdsh_-prefixed query parameters to any game URL. For example, ?wvdsh_mode=ranked would appear as { mode: "ranked" } in getLaunchParams().
Connection events
Listen for connection changes to show online/offline status in your UI:
func _ready():
WavedashSDK.backend_connected.connect(_on_connected)
WavedashSDK.backend_disconnected.connect(_on_disconnected)
WavedashSDK.backend_reconnecting.connect(_on_reconnecting)
func _on_connected(payload):
print("Connected (attempt %d)" % payload["connectionCount"])
func _on_disconnected(payload):
print("Disconnected (was ever connected: %s)" % payload["hasEverConnected"])
func _on_reconnecting(payload):
print("Reconnecting (retry %d)" % payload["connectionRetries"])void Awake()
{
Wavedash.SDK.OnBackendConnected += data =>
Debug.Log($"Connected (attempt {data["connectionCount"]})");
Wavedash.SDK.OnBackendDisconnected += data =>
Debug.Log($"Disconnected (was ever connected: {data["hasEverConnected"]})");
Wavedash.SDK.OnBackendReconnecting += data =>
Debug.Log($"Reconnecting (retry {data["connectionRetries"]})");
}function init(self)
wavedash.init({}, function(_, event, payload)
if event == wavedash.EVENT_BACKEND_CONNECTED then
print(("Connected (attempt %d)"):format(payload.connectionCount))
elseif event == wavedash.EVENT_BACKEND_DISCONNECTED then
print(("Disconnected (was ever connected: %s)"):format(tostring(payload.hasEverConnected)))
elseif event == wavedash.EVENT_BACKEND_RECONNECTING then
print(("Reconnecting (retry %d)"):format(payload.connectionRetries))
end
end)
endWavedash.on(Wavedash.Events.BACKEND_CONNECTED, (payload) => {
console.log(`Connected (attempt ${payload.connectionCount})`);
});
Wavedash.on(Wavedash.Events.BACKEND_DISCONNECTED, (payload) => {
console.log(`Disconnected (was ever connected: ${payload.hasEverConnected})`);
});
Wavedash.on(Wavedash.Events.BACKEND_RECONNECTING, (payload) => {
console.log(`Reconnecting (retry ${payload.connectionRetries})`);
});What's next
Accounts, avatars, friends, and presence.
Lobbies, chat, and P2P networking.
Reward players for hitting milestones.
Godot, Unity, Three.js, and more.
See the Events reference for the complete event list.