Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tinkerer9/collabokeys/llms.txt

Use this file to discover all available pages before exploring further.

CollaboKeys splits the keyboard across every player in the session — each person owns a distinct subset of keys, and only they can press those keys to influence the game. No single player can control everything on their own, which creates a layer of forced cooperation (and deliberate, hilarious chaos) that makes any ordinary keyboard game feel brand new.

Key Reservation

The reservation system is what makes CollaboKeys tick. The first time a player presses a key that has not yet been claimed by anyone, that key is automatically assigned to them — from that point forward, it belongs exclusively to that player.
  • allowReservationAtStart in config.json (default true) controls whether auto-reservation is active when the server starts. When it is false, players cannot claim new keys until an admin re-enables reservation.
  • Once a key is reserved, any other player who tries to press it will have their attempt rejected and will see a red message explaining that the key is already taken.
  • A player can see all of their currently reserved keys in the Keys panel on the right side of the player UI.
  • If a player refreshes the page, they disconnect and reconnect as a brand-new player, losing every key they had reserved.
  • The admin can revoke individual keys or clear all assignments at any time:
key revoke <keyname>
key revoke all
  • maxReservedKeys (default 0 = no limit) caps the number of keys any single player may claim. Once the cap is hit, further key presses are rejected with the message “You can’t reserve any more keys.”
Revoking a key returns it to the unclaimed pool instantly. The next player to press it will claim it as their own.

Player Lifecycle

Every player goes through the same series of states from the moment they open the player page to the moment they leave.
1

Connect

The player opens the server URL in their browser. The server assigns them an incrementing numeric ID (visible in the browser tab title and browser console) and emits it back via a id Socket.IO event.
2

Set a username

Before any keypresses are accepted, the player must enter a valid username in the naming prompt and click Continue. By default, usernames must be 3–20 characters long and match the regex [a-zA-Z0-9] (alphanumeric only). These limits are configurable via player.name.minLength, player.name.maxLength, and player.name.regex in config.json.
3

Press keys and reserve them

Once a name is set, the naming prompt disappears, keypress listening is enabled in the browser, and the Logs and Keys panels appear. Every key the player presses is processed by the server — new keys are reserved and emulated, already-owned keys are emulated again, and keys owned by others are rejected.
4

Waiting room (optional)

If player.waitRoomWhenJoined is true in config.json, newly connected players are held in a waiting room and cannot press keys until an admin admits them:
waitingroom admit <id>
waitingroom admit all
A dismissed player is sent back to the waiting room and must be admitted again.
5

Disconnect

When a player closes their tab or refreshes the page, the server destroys their player object and immediately frees all of their reserved keys, returning them to the unclaimed pool for other players.

Keypress Flow

When a player presses a key in their browser, the following chain of events takes place before anything happens on the host machine.
1

Browser emits a Socket.IO event

The player’s browser fires a keydown event (or keyup on release). The client script reads event.key, converts any browser aliases to canonical key names, and emits keydown or keyup to the server over Socket.IO. Repeated keydown events from held keys are filtered out with e.repeat checks.
2

Server validates the event

The server runs through a series of checks in order. If any check fails, a red log message is sent back to the player and the event is dropped:
CheckFailure message
Player has a name set(silently ignored)
Player is not in the waiting room(silently ignored)
Global emulation is enabled”Emulation is disabled by admin.”
Global keypress rate limit not exceeded”The global keypress limit has been reached. Please wait N seconds.”
Key exists in the supported list”[key] is not supported.”
Key is enabled”[key] is disabled by admin.”
Key is unclaimed or already owned by this player”Auto-reservation is disabled by admin.” / “[key] is already reserved.”
Player has not exceeded maxReservedKeys”You can’t reserve any more keys.”
Keyup events bypass most of these checks intentionally — the server only needs to confirm the player can type at all before releasing a held key, so held keys are always properly released even if emulation is toggled off mid-press.
3

Key is reserved (if new)

If the key passed all checks and was unclaimed, it is now assigned to this player in keycodes.js. The server emits a keyReserved event back to the player’s browser, which appends the key’s human-readable name to the Keys panel list.
4

Keypress is emulated on the host

The server invokes the CoreGraphics keyboard helper to send the key event to whatever application is currently active on the host Mac. The exact behavior depends on the allowHeldKeys setting:
keydown triggers keyboard.down(keyCode) and keyup triggers keyboard.up(keyCode). Keys can be physically held in the game — useful for racing or platformer movement.
5

Log messages are broadcast

The pressing player sees “You pressed [key].” in bold in their Logs panel. All other connected players see “[Username] pressed [key].” so everyone knows what is happening in real time.

Rate Limiting

CollaboKeys enforces a single global keypress rate limit shared across all players. This prevents the host machine from being flooded with emulated events.
  • maxKeypressesPerMinute (default 150) is the ceiling. Setting it to 0 disables the limit entirely.
  • The counter resets at the top of every clock minute (not a rolling window). When the limit is hit, any further keydown attempts are rejected with a message telling the player how many seconds remain until the next reset.
  • The counter only increments for successful keypresses — rejected events do not count toward the limit.
The rate limit is global, not per-player. In a session with many active players, the ceiling can be reached quickly. Consider raising maxKeypressesPerMinute for larger groups or fast-paced games.

Build docs developers (and LLMs) love