Skip to main content

Quick Start

Build a multiplayer game for the Rune platform and its millions of players. Rune handles netcode, servers, voice chat, matchmaking, spectating, and much more.

To get started, you only need to run one command:

npx rune-games-cli@latest create

After this, you'll have a multiplayer Tic Tac Toe game running.

Alternatively, follow the guide to port your existing game to Rune.

Uploading & Playing In The App

Now that you have Tic Tac Toe running locally, it would be great to try playing it in the Rune app:

npm run upload

That's it. You'll now see your game inside Rune and can play it with your friends!

Game Logic

Rune games are split into two parts: logic & rendering. Let's look at the logic for the generated Tic Tac Toe game.

You can find the logic code in the logic.js file. The setup function is responsible for creating an initial game state that's synced across players:

function setup() {
const game = {
cells: new Array(9).fill(null), // 3x3 cell grid
lastMovePlayerId: null,
// ... rest of the game state
return game

To modify the game state synced between players, we define actions that get called from the client code. Here's the action to mark a cell in Tic Tac Toe:

function claimCell(cellIndex, { game, playerId }) {
// Do not allow to claim cell if it's already claimed or if it's not player's turn
if (game.cells[cellIndex] !== null || playerId === game.lastMovePlayerId) {
throw Rune.invalidAction()

game.cells[cellIndex] = playerId
game.lastMovePlayerId = playerId

// ... rest of the logic, like checking for win condition

Finally, we provide setup function, actions, and other game info to Rune.initLogic():

minPlayers: 2,
maxPlayers: 2,
actions: {

Other initLogic() options are described in API game logic reference. You can also read a more in-depth explanation of how the logic code works in Syncing Game State.

Rendering & Inputs

You can find your game rendering code in client.js file. The client code is responsible for reacting to game state changes and updating the rendering accordingly:

function onChange({ game, players, yourPlayerId, action }) {
const { cells, lastMovePlayerId } = game

// ... update your game visuals according to latest received game state. Also play sound effects, update styles, etc.

Rune.initClient({ onChange })

The client code also calls actions based on user input:

const button = // ... get the cell
button.addEventListener("click", () => Rune.actions.claimCell(cellIndex))

You can find additional information about rendering here.

What Next?