Playable tutorials
A tutorial is a local lesson using your real engine and viewer. BGS provides the chapter list and a screen-height iframe. Your game supplies the starting position, instructions and scripted opponents. Tutorials do not create multiplayer games or affect clocks, ratings or notifications.
Install @boardgamers/protocol 0.3 or later. Include tutorials in your viewer bundle;
BGS uses the same JavaScript and CSS for games and lessons.
Define a chapter
import { createTutorial } from "@boardgamers/protocol/tutorial";
const tutorial = await createTutorial<GameState, Move>({
game: "mygame",
id: "first-move",
version: 1,
initialState: () => preparedPosition(),
move: (state, move) => engine.move(state, move, 0),
storage: localStorage,
steps: [
{ id: "welcome", title: "Your first move", text: "Place a piece on the board." },
{
id: "place",
title: "Choose a space",
text: "Click an empty space.",
target: "board",
hint: "Any empty space works.",
complete: (state) => state.board.some((cell) => cell !== null),
},
],
completion: { title: "Well done", text: "You placed your first piece!" },
});
// Render snapshots in your existing UI. Send its moves to tutorial.play(move).
const unsubscribe = tutorial.subscribe(({ state }) => render(state));
An explanation has a Continue button. An action step advances only when
complete(state) succeeds after a valid engine move. text, hint and target
may also be functions of state. Optional validateMove(state, move) returns a
short explanation if an otherwise legal move is outside the lesson; the engine
still validates all inputs.
For quiz steps, add success: "Correct! …" with a brief explanation (protocol 0.3.2+).
It appears in the guide after a correct answer, without an extra click. success
can also be a function of the resulting state. Omit target when the answers are
already beside the instructions.
Use completion for the final summary. It stays visible after the last action;
the chapter is already complete, with no extra Continue click.
Use a fixed seed. Apply scripted opponent actions inside move using the same
engine. Keep this deterministic: replaying the same choices must give the same
result. Pass only the learner's stripped view to the UI until the rules reveal
other players' information.
Register and display it
import { registerViewer } from "@boardgamers/protocol/viewer";
import { createTutorial } from "@boardgamers/protocol/tutorial";
import { mountTutorialGuide } from "@boardgamers/protocol/tutorial/dom";
registerViewer("mygame", mountGame, {
tutorial: async (target, { chapter, onProgress, nextChapter }) => {
const tutorial = await createTutorial({
...chapters[chapter],
storage: localStorage,
onProgress,
});
// Mount your game and guide inside target.
// Render tutorial snapshots and send actions to tutorial.play(move).
const removeGuide = mountTutorialGuide(guideElement, tutorial, { nextChapter });
return () => {
removeGuide();
tutorial.destroy();
// Unsubscribe and unmount your game UI.
};
},
});
Add the tutorial option to your existing registerViewer call (mountGame above).
BGS passes the selected interface language as options.locale to launchTutorial.
Localize the lesson text, hints, feedback and controls without changing chapter IDs,
move validation or saved progress. Missing or unsupported locales fall back to English.
BGS calls mygame.launchTutorial for lessons and mygame.launch for games. The library
cleans up the previous session when either starts.
The optional guide supplies progress, hints and playback controls: back to start,
previous step, replay step and continue.
Pass the host's nextChapter option to show Next chapter alongside them on
completion (protocol 0.3.3+). BGS handles navigation; standalone lessons can omit it.
Style its .bgs-tutorial-* classes to fit your game. Give highlighted elements a
stable name, such as data-tutorial="board"; the guide adds
.bgs-tutorial-highlight. Show area scrolls to it and briefly emphasizes the outline,
respecting reduced-motion preferences. Style .bgs-tutorial-feedback for success messages.
On mobile, keep the guide compact and collapsible, leaving room for the game's actions.
Let players make their moves with the ordinary game controls. Use the guide to explain what to select, and highlight the relevant controls instead of adding buttons that perform the move for them. A prepared example can preselect pieces or a route, but the player should still confirm through the game's interface. Separate buttons are useful for quiz answers or advancing scripted opponents.
You can build your own guide from tutorial.snapshot and subscribe instead.
Its controls call continue(), previousStep(), restartStep() and restart().
Going back restores the start of that step. play() returns
whether the action succeeded; failures appear in snapshot.error.
Successful-step explanations appear in snapshot.feedback until the next accepted action.
Progress and hosting
Progress resumes from browser storage. Increment a chapter’s version when its
setup or rules change enough to invalidate saved progress.
Upload your viewer as usual. In Tutorial, add chapter IDs, revisions, titles and descriptions. Players with access to that game version get a Learn to play link, including beta testers.
Chapter titles and descriptions use BGS's game-metadata translations. Translate them from the admin Translations page. Text inside the lessons is localized by your viewer.
Test lessons on desktop and mobile, including reload and retry. See the Fuji example.
Sections
For a longer course, group chapters into sections such as Basics, Federations or an expansion. In the admin tutorial editor, add sections with an ID, title and description, then assign chapters using the Section field. Sections appear in their listed order; unassigned chapters appear first. Chapter order within a section follows the chapter list. Next chapter follows that same order.
Section titles and descriptions are translated with the game metadata, just like chapter cards. Section IDs do not change chapter IDs or saved progress. Games with short tutorials can leave sections out.
Tutorials always use the primary viewer. A player’s alternate-viewer preference applies only to games.