GUIDELINES

Plain rules. We want this to feel like the old portals — fun, weird, and safe to click.

What to upload

What's not allowed

How publishing works

Saving progress (optional)

Your game can persist up to 64 KB of JSON per save slot, scoped to the signed-in player — nobody else can read or write it. Games have no network access at all (see the sandboxing note above), so saves go through the parent page via postMessage. Paste this into your HTML and call Save.get() / Save.put(data):

<script>
  let _saveReqId = 0;
  const _saveWaiters = new Map();
  window.addEventListener("message", (e) => {
    const m = e.data;
    if (!m || m.channel !== "domainless-save" || m.type !== "result") return;
    const w = _saveWaiters.get(m.reqId);
    if (!w) return;
    _saveWaiters.delete(m.reqId);
    m.ok ? w.resolve(m.data) : w.reject(new Error(m.error));
  });
  function _saveCall(type, extra) {
    return new Promise((resolve, reject) => {
      const reqId = ++_saveReqId;
      _saveWaiters.set(reqId, { resolve, reject });
      window.parent.postMessage(
        Object.assign({ channel: "domainless-save", type, reqId }, extra),
        "*",
      );
    });
  }
  const Save = {
    get: (slot = "default") => _saveCall("get", { slot }),
    put: (data, slot = "default", version = 1) =>
      _saveCall("put", { slot, data, version }),
    del: (slot = "default") => _saveCall("delete", { slot }),
    list: () => _saveCall("list", {}),
  };
  // await Save.put({ level: 3, coins: 120 });
  // const saved = await Save.get(); // { slot, version, data, updated_at } or null
</script>

Not signed in? Save.get() / Save.put() reject with an error your game can catch (e.g. show "sign in to save progress"). Slot names: letters, numbers, - and _, up to 40 characters — use more than one slot for multiple save files. version is yours to use however you like (e.g. bump it when you change your save format).

Questions? Email us.

Submit a game Back to arcade