---
type: tutorial
title: Bâtir un jeu pour le marketplace
summary: "Bâtis un jeu Caputchin à partir d'un dossier vide : installe le SDK, enregistre une fabrique, rends à partir du contexte par manche, enregistre une trace, et empaquette-le pour l'iframe en bac à sable."
---

À la fin de ce tutoriel, tu auras un jeu Caputchin complet et jouable dans un seul bundle JavaScript, prêt à être rendu rejouable et publié. C'est la version ennuyeuse, fais-chaque-étape ; lis d'abord [Livre un jeu au marketplace](/docs/marketplace-game-development/overview) pour savoir où ça s'insère.

Il te faut Node et un bundler (esbuild, rollup, vite ou webpack, n'importe lequel). Tu n'as pas besoin d'un compte Caputchin pour bâtir, seulement pour publier.

## 1. Installe le SDK

```bash
npm install @caputchin/game-sdk
```

Le SDK est minuscule : un assistant `register` plus des types TypeScript. Il n'embarque pas le runtime du widget, donc ton jeu reste petit.

## 2. Enregistre une fabrique de jeu

Un jeu est un seul appel à `register` avec une **fonction de fabrique**. Le widget invoque ta fabrique dans l'iframe en bac à sable ; cet appel est lui-même le signal de départ (il n'y a pas d'événement de départ distinct à attendre).

```js
import { register } from "@caputchin/game-sdk";

register((container, bridge, ctx) => {
  // container - a DOM element inside the sandboxed iframe; render into it.
  // bridge    - push-only channel to the host (pass / error / setSize).
  // ctx       - the per-round context (seed + resolved locale/skin/config).

  const cleanup = startGame(container, bridge, ctx);

  // Return an optional cleanup function; the widget calls it on unmount.
  return cleanup;
});
```

Tu ne passes **pas** le manifeste à `register` ; le serveur lit `caputchin.json` au moment de l'indexation et livre les préréglages résolus à ta fabrique comme `ctx`. Vois la [référence du SDK](/docs/marketplace-game-development/game-sdk-reference) pour la surface complète.

## 3. Rends à partir du contexte par manche

Le troisième argument de la fabrique, `ctx`, porte tout ce qui change par visiteur :

- `ctx.seed` - la graine par manche. **Dérive tout l'aléatoire de celle-ci** (vois l'étape 5). `null` hors d'une session vérifiée.
- `ctx.locale` - les chaînes de langue résolues (`ctx.locale._lang` plus tes clés), ou `null`.
- `ctx.skin` - les couleurs et URL de ressources résolues (`ctx.skin._theme`, toujours `light` ou `dark`, plus tes clés), ou `null`.
- `ctx.config` - la configuration de gameplay résolue, ou `null`.

Lis tes propres clés dessus ; tu ne résous jamais les préréglages toi-même. Chacun est `null` quand ton manifeste ne déclare aucun bloc correspondant, donc retombe toujours sur un défaut intégré :

```js
function startGame(container, bridge, ctx) {
  const title = ctx.locale?.title ?? "Tap the targets";
  const accent = ctx.skin?.accent_color ?? "#2da44e";
  const targetCount = ctx.config?.target_count ?? 5;
  // ...render with these...
}
```

Si ta mise en page a besoin d'une taille explicite, appelle `bridge.setSize(width, height)` une fois après ton premier rendu.

## 4. Victoire, défaite, et erreurs

Ton jeu décide quand le joueur gagne et le dit à l'hôte via le pont :

- À la victoire, appelle `bridge.pass({ trace })` avec la [trace](#5-record-a-trace) de la manche (étape 5).
- À la défaite ou à l'abandon, n'appelle **rien** ; le silence est le signal d'échec.
- Si le jeu lui-même casse (une ressource a échoué, une exception), appelle `bridge.error({ code, message })`. C'est pour une défaillance interne au jeu, pas pour un joueur qui perd.

```js
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}
```

## 5. Enregistre une trace [#5-record-a-trace]

C'est l'étape qui rend le jeu **rejouable**. À mesure que le joueur agit, enregistre les entrées qui pilotent le résultat (quelle cible il a touchée, dans l'ordre, avec le timing si ça compte) et sérialise cet enregistrement en une chaîne ou un tableau d'octets : c'est ta **trace**. Combinée à la graine, la trace doit laisser ta logique reproduire le résultat exact, parce que le serveur la réexécute.

Deux règles rendent ça possible :

1. **Dérive chaque choix aléatoire de `ctx.seed`.** N'appelle jamais `Math.random()` ni ne lis l'horloge murale pour quoi que ce soit qui affecte le résultat.
2. **Enregistre assez pour rejouer.** La trace plus la graine est l'entrée complète de ta logique de jeu.

```js
function makeRng(seed) {
  // seed is ctx.seed; feed it so the server reproduces the same sequence.
  let s = hashSeed(seed);
  return () => (s = (s * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
}
```

La forme de `seed`, de la trace et du verdict est définie par [le contrat de rejeu](/docs/marketplace-game-development/replay-contract) ; cette page couvre la transformation de ce jeu enregistré en l'artefact `run` headless que le serveur rejoue. Si écrire à la main une logique déterministe semble laborieux, le [kit moteur](/docs/marketplace-game-development/engine-kit) le fait pour toi, optionnellement.

## 6. Bundle pour le bac à sable

Le widget charge exactement une URL de script, donc tout doit être dans ce seul fichier. Configure ton bundler pour une seule sortie autonome :

- intègre les ressources comme URL de données (sprites, sons, polices) ;
- désactive le découpage de code ;
- si tu utilises WASM, intègre-le en base64 et instancie à partir des octets.

Les schémas qui **ne** fonctionnent **pas** dans l'iframe à origine opaque et à CSP stricte :

- un `fetch('./sprite.png')` relatif au chemin - il n'y a pas de chemin d'où récupérer ;
- un `import('./chunk.js')` dynamique - la deuxième URL est bloquée ;
- `new Worker('./worker.js')` - lance plutôt les workers à partir d'URL `Blob` en ligne ;
- les récupérations de CDN externe à l'exécution - `connect-src` les bloque.

## 7. Teste-le en local

Il n'y a pas de harnais spécial. Intègre le widget dans une page HTML statique, pointe `game-src` vers ta sortie de bundle locale, et joue-le dans un navigateur :

```html
<caputchin-game sitekey="cpt_pub_..." game-src="http://localhost:8080/game.js"></caputchin-game>
```

C'est le même chemin `game-src` qu'un [jeu personnalisé](/docs/custom-game-development/self-hosted-game) utilise ; pour le marketplace, tu publieras plutôt le dépôt pour que la plateforme épingle le bundle pour toi.

## Étapes suivantes

- [Le contrat de rejeu](/docs/marketplace-game-development/replay-contract) : transforme ton jeu enregistré en l'artefact `run` qui rend le jeu rejouable.
- [Le manifeste caputchin.json](/docs/marketplace-game-development/the-caputchin-json-manifest) : décris le jeu, ses préréglages et son bundle.
- [Publier au marketplace](/docs/marketplace-game-development/publish-to-the-marketplace) : tague le dépôt et passe en ligne.

## Voir aussi

- [Référence du SDK de jeu](/docs/marketplace-game-development/game-sdk-reference) : chaque export, en entier.
- [L'engine-kit](/docs/marketplace-game-development/engine-kit) : la façon optionnelle d'obtenir le déterminisme gratuitement.
