Getting Started
Get up and running with CTRLLR in a few minutes.
Installation
Install the core package using your preferred package manager:
# pnpm
pnpm add @ctrllr/core
# npm
npm install @ctrllr/core
# yarn
yarn add @ctrllr/core
Quick Start
1. Initialize the Manager
import { CtrllrManager } from '@ctrllr/core';
const ctrllr = new CtrllrManager({
signalingUrl: 'wss://your-signaling-server.com',
});
2. Connect to the Signaling Server
await ctrllr.connect();
3. Display a QR Code
Generate a QR code for players to scan with the CTRLLR mobile app:
const qrImg = document.getElementById('qr') as HTMLImageElement;
qrImg.src = await ctrllr.getQRCodeDataURL();
4. Handle Controller Connections
ctrllr.on('controllerconnected', (e) => {
console.log(`Player ${e.controller.index} joined!`);
console.log(`Username: ${e.controller.username}`);
});
ctrllr.on('controllerdisconnected', (e) => {
console.log(`Player ${e.controller.index} left`);
});
Controller Input
Each controller has a joystick and 4 aimable buttons (A, X, Y, Z):
interface ControllerState {
joystick: InputState; // Left analog stick
a: InputState; // Aimable button A
x: InputState; // Aimable button X
y: InputState; // Aimable button Y
z: InputState; // Aimable button Z
}
interface InputState {
pressed: boolean; // Is the input active
x: number; // Horizontal axis (-1 to 1)
y: number; // Vertical axis (-1 to 1)
}
Reading Input
Game Loop (Polling)
Read state directly in your game loop for continuous input like movement:
function gameLoop() {
for (const controller of ctrllr.controllers.values()) {
const { joystick } = controller.state;
player.move(joystick.x, joystick.y);
// Aimable buttons have direction too
if (controller.state.a.pressed) {
player.aim(controller.state.a.x, controller.state.a.y);
}
}
requestAnimationFrame(gameLoop);
}
Events (Discrete Actions)
Use events for button presses — the SDK handles edge detection:
controller.on('buttondown', (e) => {
if (e.input === 'a') player.jump();
if (e.input === 'x') player.shoot(e.x, e.y); // e.x, e.y = aim direction
});
controller.on('buttonup', (e) => {
if (e.input === 'a') player.endJump();
});
// State change (useful for React setState)
controller.on('statechange', (e) => {
setControllerState(e.state);
});
Manager-Level Events
Listen on the manager for "any controller" scenarios:
// Pause menu - any controller can pause
ctrllr.on('buttondown', (e) => {
if (e.input === 'a') {
game.togglePause();
}
});
// "Press A to join" lobby
ctrllr.on('buttondown', (e) => {
if (e.input === 'a') {
lobby.playerReady(e.controller);
}
});
React Integration
Create a custom hook to sync controller state with React:
import { useState, useEffect } from 'react';
import { Controller, ControllerState } from '@ctrllr/core';
function useController(controller: Controller) {
const [state, setState] = useState<ControllerState>(controller.state);
useEffect(() => {
const handler = (e: { state: ControllerState }) => setState(e.state);
controller.on('statechange', handler);
return () => controller.off('statechange', handler);
}, [controller]);
return state;
}
Then use it in your components:
function PlayerHUD({ controller }: { controller: Controller }) {
const state = useController(controller);
return (
<div>
<div>
Joystick: {state.joystick.x.toFixed(2)}, {state.joystick.y.toFixed(2)}
</div>
<div>A Button: {state.a.pressed ? 'Pressed' : 'Released'}</div>
</div>
);
}
QR Code Options
Customize the QR code appearance:
const qrDataUrl = await ctrllr.getQRCodeDataURL({
width: 256, // Size in pixels (default: 256)
margin: 2, // Margin in modules (default: 2)
darkColor: '#000000', // Foreground color
lightColor: '#ffffff', // Background color
});
// Or get SVG
const qrSvg = await ctrllr.getQRCodeSVG();
Full Example
Here's a complete setup example:
import { CtrllrManager } from '@ctrllr/core';
async function init() {
const ctrllr = new CtrllrManager({
signalingUrl: 'wss://your-signaling-server.com',
});
await ctrllr.connect();
// Show QR code
const qrImg = document.getElementById('qr') as HTMLImageElement;
qrImg.src = await ctrllr.getQRCodeDataURL();
// Handle connections
ctrllr.on('controllerconnected', ({ controller }) => {
console.log(`${controller.username} joined as Player ${controller.index}`);
// Listen to this controller's events
controller.on('buttondown', (e) => {
console.log(`Button ${e.input} pressed at (${e.x}, ${e.y})`);
});
controller.on('statechange', (e) => {
// Access full state
console.log('Joystick:', e.state.joystick);
});
});
ctrllr.on('controllerdisconnected', ({ controller }) => {
console.log(`Player ${controller.index} disconnected`);
});
// Game loop
function gameLoop() {
for (const controller of ctrllr.controllers.values()) {
const { joystick, a } = controller.state;
// Move with joystick
if (joystick.x !== 0 || joystick.y !== 0) {
player.move(joystick.x, joystick.y);
}
// Aim with A button
if (a.pressed) {
player.aim(a.x, a.y);
}
}
requestAnimationFrame(gameLoop);
}
gameLoop();
}
init();
Next up: explore the full API Reference (coming soon).