---
type: tutorial
title: Construye un juego del marketplace
summary: "Construye un juego de Caputchin desde una carpeta vacía: instala el SDK, registra una factory, renderiza desde el contexto por ronda, registra una traza, y empaquétalo para el iframe en sandbox."
---

Al final de este tutorial tendrás un juego de Caputchin completo y jugable en un bundle de JavaScript, listo para hacerse reproducible y publicar. Esta es la versión aburrida de haz-cada-paso; lee [Publica un juego en el marketplace](/docs/marketplace-game-development/overview) primero para saber dónde encaja.

Necesitas Node y un bundler (esbuild, rollup, vite o webpack, cualquiera de ellos). No necesitas una cuenta de Caputchin para construir, solo para publicar.

## 1. Instala el SDK

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

El SDK es diminuto: un helper `register` más tipos de TypeScript. No empaqueta el runtime del widget, así que tu juego se queda pequeño.

## 2. Registra una factory de juego

Un juego es una sola llamada a `register` con una **función factory**. El widget invoca tu factory dentro del iframe en sandbox; esa llamada es en sí la señal de inicio (no hay un evento de inicio separado que esperar).

```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;
});
```

**No** pasas el manifiesto a `register`; el servidor lee `caputchin.json` en tiempo de indexación y envía los presets resueltos a tu factory como `ctx`. Mira la [referencia del SDK](/docs/marketplace-game-development/game-sdk-reference) para la superficie completa.

## 3. Renderiza desde el contexto por ronda

El tercer argumento de la factory, `ctx`, lleva todo lo que cambia por visitante:

- `ctx.seed` - la semilla por ronda. **Deriva toda la aleatoriedad de esto** (mira el paso 5). `null` fuera de una sesión verificada.
- `ctx.locale` - cadenas de idioma resueltas (`ctx.locale._lang` más tus claves), o `null`.
- `ctx.skin` - colores y URLs de assets resueltos (`ctx.skin._theme`, siempre `light` o `dark`, más tus claves), o `null`.
- `ctx.config` - configuración de juego resuelta, o `null`.

Lee tus propias claves de estos; nunca resuelves presets tú mismo. Cada uno es `null` cuando tu manifiesto no declara ningún bloque coincidente, así que recae siempre en un valor por defecto integrado:

```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 tu layout necesita un tamaño explícito, llama a `bridge.setSize(width, height)` una vez tras tu primer pintado.

## 4. Ganar, perder y errores

Tu juego decide cuándo gana el jugador y se lo dice al anfitrión a través del bridge:

- Al ganar, llama a `bridge.pass({ trace })` con la [traza](#5-record-a-trace) de la ronda (paso 5).
- En una derrota o abandono, llama a **nada**; el silencio es la señal de fallo.
- Si el propio juego se rompe (un asset falló, una excepción), llama a `bridge.error({ code, message })`. Eso es para un fallo interno del juego, no para que un jugador pierda.

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

## 5. Registra una traza [#5-record-a-trace]

Este es el paso que hace el juego **reproducible**. Mientras el jugador actúa, registra las entradas que conducen el desenlace (qué objetivo golpeó, en orden, con temporización si importa) y serializa ese registro a una cadena o un array de bytes: esa es tu **traza**. Combinada con la semilla, la traza debe dejar que tu lógica reproduzca el resultado exacto, porque el servidor la vuelve a correr.

Dos reglas lo hacen posible:

1. **Deriva cada elección aleatoria de `ctx.seed`.** Nunca llames a `Math.random()` ni leas el reloj de pared para nada que afecte al desenlace.
2. **Registra lo suficiente para repetir.** La traza más la semilla es la entrada completa a la lógica de tu juego.

```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 forma de `seed`, la traza, y el veredicto las define [el contrato de repetición](/docs/marketplace-game-development/replay-contract); esa página cubre convertir este juego registrado en el artefacto `run` headless que el servidor repite. Si escribir a mano lógica determinista suena engorroso, el [engine kit](/docs/marketplace-game-development/engine-kit) lo hace por ti, opcionalmente.

## 6. Empaqueta para el sandbox

El widget carga exactamente una URL de script, así que todo debe estar en ese único archivo. Configura tu bundler para una sola salida autocontenida:

- inlinea los assets como data URLs (sprites, sonidos, fuentes);
- desactiva el code splitting;
- si usas WASM, embébelo como base64 e instáncialo desde bytes.

Patrones que **no** funcionan dentro del iframe de origen opaco y CSP estricta:

- `fetch('./sprite.png')` relativo a la ruta - no hay ruta desde la que descargar;
- `import('./chunk.js')` dinámico - la segunda URL está bloqueada;
- `new Worker('./worker.js')` - lanza workers desde URLs `Blob` inline en su lugar;
- descargas de CDN externo en tiempo de ejecución - `connect-src` las bloquea.

## 7. Pruébalo en local

No hay harness especial. Incrusta el widget en una página HTML estática, apunta `game-src` a la salida de tu bundle local, y juégalo en un navegador:

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

Este es el mismo camino `game-src` que usa un [juego a medida](/docs/custom-game-development/self-hosted-game); para el marketplace en su lugar publicarás el repo para que la plataforma fije el bundle por ti.

## Siguientes pasos

- [El contrato de repetición](/docs/marketplace-game-development/replay-contract): convierte tu juego registrado en el artefacto `run` que hace el juego reproducible.
- [El manifiesto caputchin.json](/docs/marketplace-game-development/the-caputchin-json-manifest): describe el juego, sus presets, y su bundle.
- [Publica en el marketplace](/docs/marketplace-game-development/publish-to-the-marketplace): etiqueta el repo y sal en vivo.

## Véase también

- [Referencia del game SDK](/docs/marketplace-game-development/game-sdk-reference): cada export, por completo.
- [El engine-kit](/docs/marketplace-game-development/engine-kit): la forma opcional de obtener determinismo gratis.
