# Referencia del motor de Pixelbook (API v1)

Todo lo que un cartucho puede usar para dibujar, además de la API de la consola (`API.md`). Se genera a partir del motor, así que siempre coincide con lo que corre en la app. La pantalla mide 480 × 270 píxeles de arte, con el origen arriba a la izquierda; en la app cada píxel se ve como un bloque. Los colores son índices de una paleta cerrada: se eligen por rampa (`C.teal[3]`), nunca en RGB.

Convenciones que se repiten:

- `t`: tiempo de la historia en segundos; `lt`: tiempo dentro de la escena; `NOW`: tiempo de sesión (sigue corriendo con la historia parada en un reto).
- `o = {}` al final de una firma: opciones con nombre; el comentario dice cuáles hay.
- Rampa: lista de colores de oscuro (0) a claro. `ramp` en una opción es una rampa entera (`C.green`); `c`, `fill` o `col` son un color (`C.green[3]`).
- Animar: con `win(t, t0, dur, E.out)` (0 a 1 entre t0 y t0 + dur), `M(escena, marca)` y `L(voz).t` / `Lend(voz)` para sincronizar con el guion. Nunca con tiempos escritos a mano: las voces reales duran distinto que la estimación.

## Paleta

Rampas (`C.<nombre>[i]`, de 0 oscuro a claro):

| Rampa | Colores |
| --- | --- |
| `ink` | 0: `#0b0816` · 1: `#171127` · 2: `#231a39` · 3: `#322950` |
| `gray` | 0: `#463f66` · 1: `#625e86` · 2: `#8584a8` · 3: `#abaec6` · 4: `#d3d7e4` · 5: `#f7f6ef` |
| `warmgray` | 0: `#3f3434` · 1: `#5d4f4a` · 2: `#827167` · 3: `#a9998a` · 4: `#cfc3b0` · 5: `#ece5d3` |
| `red` | 0: `#4a0e22` · 1: `#861b31` · 2: `#c52e3b` · 3: `#ee5a4b` · 4: `#ff9277` |
| `orange` | 0: `#662812` · 1: `#aa4718` · 2: `#e27526` · 3: `#ffa13d` · 4: `#ffcf7c` |
| `yellow` | 0: `#6e4a14` · 1: `#b5841d` · 2: `#efbe2e` · 3: `#ffe45f` · 4: `#fff6b7` |
| `lime` | 0: `#2b3f12` · 1: `#4c7016` · 2: `#7aa41f` · 3: `#a6cd36` · 4: `#d4ef7c` |
| `green` | 0: `#0b3024` · 1: `#12573a` · 2: `#1f834c` · 3: `#3bb460` · 4: `#82de88` |
| `teal` | 0: `#08293a` · 1: `#0d4c61` · 2: `#13768a` · 3: `#20a6b0` · 4: `#6ee0d5` |
| `blue` | 0: `#0e1c48` · 1: `#163378` · 2: `#2154b4` · 3: `#3a83e4` · 4: `#7bbdff` |
| `purple` | 0: `#1f1244` · 1: `#3c2180` · 2: `#6134b4` · 3: `#8f5edd` · 4: `#c29aff` |
| `pink` | 0: `#471136` · 1: `#841f59` · 2: `#c43a80` · 3: `#ee69a6` · 4: `#ffb1d1` |
| `wood` | 0: `#2a1812` · 1: `#48291a` · 2: `#6c3f24` · 3: `#925a30` · 4: `#b97d44` · 5: `#dba664` |
| `skinA` | 0: `#5c2c26` · 1: `#8c4a3a` · 2: `#bf7155` · 3: `#e4a07f` · 4: `#f9cfad` |
| `skinB` | 0: `#3e1e1a` · 1: `#62352a` · 2: `#8c533a` · 3: `#b87650` · 4: `#dba06f` |
| `hairR` | 0: `#2c1210` · 1: `#52221a` · 2: `#7e3a22` · 3: `#a8562b` |
| `hairD` | 0: `#140c14` · 1: `#26181f` · 2: `#3a2629` · 3: `#553a36` |
| `sky` | 0: `#2f5cc4` · 1: `#4d8fe6` · 2: `#83c3f6` · 3: `#c2ebff` |
| `marble` | 0: `#b9b2a3` · 1: `#d9d2c1` · 2: `#efe9da` · 3: `#fffaf0` |
| `gold` | 0: `#5a3a10` · 1: `#94661a` · 2: `#d19f2c` · 3: `#f3d05a` · 4: `#fff1a6` |
| `tvNavy` | 0: `#040816` · 1: `#0a1430` · 2: `#10204a` · 3: `#183066` · 4: `#224488` |
| `tvBlue` | 0: `#1a4fc0` · 1: `#2f7cf0` · 2: `#5aa5ff` · 3: `#a4d4ff` |
| `tvCyan` | 0: `#0b8fb0` · 1: `#14c4e6` · 2: `#8ef2ff` |
| `tvRed` | 0: `#4d0612` · 1: `#8e0d20` · 2: `#d6182e` · 3: `#ff4a52` · 4: `#ff9da0` |
| `tvGold` | 0: `#6e4a00` · 1: `#b98200` · 2: `#ffbf1f` · 3: `#ffe07a` · 4: `#fff5cc` |
| `chrome` | 0: `#262b36` · 1: `#4b5262` · 2: `#7d8698` · 3: `#b5bdcc` · 4: `#e3e8f1` |
| `tvLand` | 0: `#1d5e2e` · 1: `#2f8a3c` · 2: `#4fb34f` · 3: `#8fd66a` |
| `noche` | 0: `#06060d` · 1: `#0c0d1b` · 2: `#14172c` · 3: `#1d2340` · 4: `#2b3458` · 5: `#414d79` |
| `lluvia` | 0: `#3a4a6e` · 1: `#5f739c` · 2: `#93a8c8` |
| `ambar` | 0: `#3a1d06` · 1: `#72390b` · 2: `#b8641a` · 3: `#eb9a32` · 4: `#ffcf73` · 5: `#fff0c4` |
| `corcho` | 0: `#3f2614` · 1: `#633d21` · 2: `#8a5a33` · 3: `#b1804f` · 4: `#d2a777` |
| `papel` | 0: `#7a6d55` · 1: `#ad9f80` · 2: `#d8cba6` · 3: `#f3e9ca` |
| `hilo` | 0: `#520d10` · 1: `#8f171b` · 2: `#cf2c27` · 3: `#ff5b4a` |
| `mapaViejo` | 0: `#6f8c86` · 1: `#94ada3` · 2: `#c2b48d` · 3: `#ddd0a8` |
| `perg` | 0: `#5e4b31` · 1: `#94784f` · 2: `#c4a878` · 3: `#e2cfa2` · 4: `#f3e6c4` · 5: `#fdf6e3` |
| `sepia` | 0: `#24170e` · 1: `#3f2a1a` · 2: `#65452c` · 3: `#8a6446` |
| `indigo` | 0: `#18233f` · 1: `#283c6b` · 2: `#41609e` · 3: `#6f8fc8` |
| `terracota` | 0: `#6b2716` · 1: `#a53f25` · 2: `#d8683f` · 3: `#f09a70` |
| `acuarela` | 0: `#4d7f88` · 1: `#6f9ea3` · 2: `#98c0bd` · 3: `#c3dcd3` |
| `oliva` | 0: `#3e5724` · 1: `#62803a` · 2: `#93ad5f` · 3: `#c3d494` |
| `cuero` | 0: `#2e150e` · 1: `#4f2718` · 2: `#733a22` · 3: `#95532f` · 4: `#b4703f` |
| `mesa` | 0: `#24150c` · 1: `#3a2415` · 2: `#553620` · 3: `#704a2c` · 4: `#8c6039` |
| `oro` | 0: `#5a3f12` · 1: `#8f6a22` · 2: `#c99a3a` · 3: `#ebc66a` · 4: `#fbe7a8` |

Atajos en `K`: `black`, `ink`, `dark`, `white`, `paper`, `hl`, `phase`, `who`. Por ejemplo, `K.white`, `K.black` o `K.who.bit` (la rampa del color de Bit).

## Iconos, pictogramas, personajes y lugares

- `icon(nombre, cx, cy, s, ramp, o)`: `trophy`, `eye`, `chart`, `flask`, `bulb`, `heart`, `stopwatch`, `book`, `check`, `cross`, `star`, `sun`, `lock`, `magnifier`, `floppy`, `warning`, `loop`, `question`, `note`, `thermo`, `gear`, `microscope`, `ruler`, `pendulum`, `megaphone`, `flame`, `chest`, `shield`, `crown`, `bolt`, `cards`, `pause`, `speaker`, `telescope`, `robot`.
- `picto(nombre, cx, cy, o)`: `factory`, `truck`, `shop`, `person`, `bag`, `cart`, `laptop`, `flower`, `doctor`, `bus`, `tractor`, `cow`, `boat`, `tree`, `mine`, `power`, `crane`, `hospital`, `school`, `plane`, `sun`, `coin`, `coins`, `bread`, `phone`, `scissors`, `padlock`, `shield`, `bug`, `house`, `state`, `briefcase`, `gear`, `chart`, `card`, `video`, `wifi`, `cheese`, `hotel`, `pension`, `road`, `train`, `trash`, `jobless`, `drop`, `oven`, `solar`, `oil`, `mineral`, `bank`, `robot`, `flask`, `bulb`, `data`, `ai`, `ship`, `anchor`, `pin`, `contacts`, `mic`, `camera`, `flashlight`, `puzzle`, `student`, `handshake`, `contract`, `strike`, `clock`, `law`, `calc`, `worker`, `sack`.
- Retratos (`bust(PERSONA, …)`, con `VEGA`, `GALILEO`, `MILAGROS` y `DANIEL`): `vega`, `galileo`, `milagros`, `daniel`, `lia`. Bit es `bit(x, y, o)`, MEGA UV es `megaUV(cx, cy, r, st)` y el retrato de quien habla es `speakerDock`.
- Lugares del mapamundi (`PLACES`): `nuevaYork`, `madrid`, `valencia`, `alicante`, `shanghai`, `pekin`, `tokio`, `londres`, `rotterdam`, `venecia`, `estambul`, `moscu`, `xian`, `urumqi`, `kashgar`, `yibuti`, `mombasa`, `nairobi`, `colombo`, `calcuta`, `kualaLumpur`, `yakarta`, `hanoi`, `quanzhou`, `gwadar`, `teheran`, `atenas`, `dakar`, `singapur`, `suez`, `losAngeles`, `saoPaulo`, `lagos`, `bombay`, `sidney`, `hongKong`.
- Transiciones de entrada de una escena (`defScene(id, draw, { in })`): `cut`, `dissolve`, `wipe`, `iris`, `blocks`, `glitch`, `black`, `page`, `tv`.

## Funciones

### core

Núcleo: matemáticas, ruido determinista, curvas de animación y acceso a la línea de tiempo del episodio. Cada fotograma es una función pura del tiempo t: el render reparte el vídeo en tramos paralelos.

- `clamp(v, a = 0, b = 1)`
- `lerp(a, b, t)`
- `inv(a, b, v)`
- `sstep(a, b, v)`
- `win(t, a, d, ease = E.io)`: ventana de una acción: 0 antes de a, 1 después de a + d
- `pulse(t, a, b, up = 0.2, down = 0.2)`: pulso: sube en [a, a + up], se mantiene y baja en [b - down, b]
- `hash32(n)`
- `rnd(a, b = 0, c = 0)`
- `srnd(a, b, c)`
- `noise1(x, seed = 0)`
- `noise2(x, y, seed = 0)`
- `kf(t, keys)`: interpolación de claves: [[t, valor, curva?], ...]; el valor puede ser número o array
- `P12(t)`: poses a 12 imágenes por segundo
- `P8(t)`
- `M(scene, mark)`
- `L(id)`
- `Lend(id)`
- `W(id, word, nth = 0, end = false)`
- `mouthWho(who, t)`
- `talking(who, t, pad = 0.05)`

### pal

Paleta cerrada de MODO CIENCIA: rampas de oscuro a claro con desplazamiento de tono (las sombras tiran a violeta, las luces a amarillo). Todo se dibuja con índices de esta paleta; nunca con colores sueltos. Los colores de las cinco fases del método científico siguen al libro: observación lima, hipótesis azul, experimentación naranja, análisis verde oscuro y difusión rojo.

- `palIndex(hex)`
- `ramp(name, hexes)`
- `dith(x, y, level)`
- `nearestIdx(r, g, b)`: Tablas de color: oscurecer, aclarar y teñir sin salir de la paleta (índice → índice)
- `lutMix(tr, tg, tb, k)`
- `lutDark(k)`: hacia el negro violeta
- `lutLight(k)`: hacia el blanco cálido

### fb

Búfer de píxeles indexado (480 x 270, un byte por píxel = índice de paleta) y primitivas sin antialias. Las coordenadas son del mundo: se les resta la cámara (CAM). La interfaz se dibuja con ui(fn), sin cámara.

- `setCam(x, y = 0)`
- `ui(fn)`
- `withCam(x, y, fn)`
- `withClip(x, y, w, h, fn)`
- `buffer(name)`
- `cls(c)`
- `pxs(sx, sy, c)`
- `px(x, y, c)`
- `getPx(x, y)`
- `rect(x, y, w, h, c)`
- `rectD(x, y, w, h, c1, c2, k)`: rectángulo tramado: mezcla c1 y c2 (k = parte de c2, 0..1) con el patrón Bayer anclado a la pantalla
- `fillFn(x, y, w, h, fn)`: recorre los píxeles de pantalla de un rectángulo y llama a fn(x, y, u, v) con coordenadas del mundo; si devuelve un índice >= 0, lo pinta
- `mapRect(x, y, w, h, lut)`: aplica una tabla de color a lo ya pintado
- `mapRectD(x, y, w, h, lut, k)`
- `hline(x0, x1, y, c)`
- `vline(x, y0, y1, c)`
- `frame(x, y, w, h, c)`
- `line(x0, y0, x1, y1, c)`: línea de un píxel (Bresenham); con fn(x, y, t) en lugar de color, llama a fn
- `lineW(x0, y0, x1, y1, w, c)`: línea gruesa (cápsula) de grosor w
- `circ(cx, cy, r, c)`: círculo relleno de aspecto limpio en píxel
- `ring(cx, cy, r, c, th = 1)`
- `ellipse(cx, cy, rx, ry, c)`: elipse rellena; con fn(x, y, u, v) en lugar de color (u, v en -1..1) para sombrear
- `poly(pts, c)`: polígono relleno (par-impar, centros de píxel)
- `polyline(P, c)`
- `rrect(x, y, w, h, r, c)`: rectángulo de esquinas redondeadas (r en píxeles)
- `vgrad(x, y, w, h, cols, off = 0)`: degradado vertical tramado por una lista de colores
- `sprite(name, rows, map)`
- `spr(s, x, y, o = {})`: dibuja un sprite (o: flip, lut, sil = color de silueta, scale entero)
- `outlined(c, fn, diag = false)`
- `outlinedRun(c, fn, diag)`
- `shadowed(c, dx, dy, fn)`: sombra proyectada: dibuja fn desplazado (dx, dy) como silueta de color c y luego fn encima

### font

Fuente de píxel propia (rejilla 5 x 7 con minúsculas, ascendentes y descendentes) para todo el texto en pantalla. Filas de la caja: 0-1 tildes de las mayúsculas, 2-8 cuerpo de las mayúsculas, 4-8 cuerpo de las minúsculas, 9-10 descendentes. drawText coloca y en la fila 2 (el techo de las mayúsculas). Se amplía ×2 o ×3 con s.

- `glyph(ch)`
- `textWidth(str, s = 1)`
- `wrapText(str, maxW, s = 1)`: parte un texto en líneas que caben en maxW
- `drawText(str, x, y, o = {})`: dibuja texto; y es el techo de las mayúsculas. o: { s, c, align, ol (contorno), sh (sombra), cf(i, ch) → color o -1, off(i, ch) → [dx, dy], upto (número de letras visibles), grad: [colores por fila], diag }

### ui

Kit de interfaz de videojuego: paneles, bocadillos, etiquetas, barras, cuenta atrás, sellos, logros, fondos animados, líneas de velocidad, confeti y destellos. Todo en coordenadas de pantalla (llamar dentro de ui() o con la cámara a 0).

- `panel(x, y, w, h, fill, o = {})`: panel con contorno oscuro, bisel claro arriba y sombra abajo; o: { r (radio), shadow, bevel, border }
- `tag(str, x, y, fill, o = {})`: etiqueta redondeada con texto centrado; devuelve su ancho
- `bubble(x, y, w, h, tx, ty, o = {})`: bocadillo con cola hacia (tx, ty)
- `bar(x, y, w, h, k, fill, o = {})`: barra de progreso con marco
- `countdown(cx, cy, r, lt, n, o = {})`: cuenta atrás circular: n segundos en total, lt tiempo transcurrido
- `stamp(cx, cy, str, col, lt, o = {})`: sello que cae de golpe (k: 0..1 desde el golpe); texto en dos tamaños según la fase del golpe
- `achievement(t, t0, title, sub, o = {})`: cartel de logro que baja desde arriba
- `gridBg(t, base, line, o = {})`: fondo de rejilla que se desplaza (pantallas de interfaz)
- `stripesBg(t, c1, c2, o = {})`: rayas diagonales que se desplazan
- `speedLines(cx, cy, t, col, o = {})`: líneas de velocidad radiales hacia (cx, cy)
- `confetti(t, t0, o = {})`: confeti de píxeles que cae desde t0
- `sparkle(x, y, t, col, o = {})`: destello de cuatro puntas (estrella de píxel) que late
- `arrow(x0, y0, x1, y1, col, o = {})`: flecha gruesa de (x0, y0) a (x1, y1)
- `slamText(str, cx, cy, lt, o = {})`: texto que entra de golpe desde grande (estilo «slam»): escala 3 → 2 en los primeros fotogramas
- `typed(str, lt, cps = 30)`: escribe letra a letra (máquina de escribir): cps letras por segundo

### icons

Iconos dibujados con primitivas a cualquier tamaño, con contorno oscuro. icon(nombre, cx, cy, tamaño, rampa) La rampa da el color base (índices 1 oscuro, 2 medio, 3 claro, 4 brillo).

- `icon(name, cx, cy, s, rmp = C.yellow, o = {})`
- `iconTrophy(x, y, rmp)`

### world

Mapamundi propio: costas simplificadas en [longitud, latitud] (precisión de unas décimas de grado, suficiente para mapas de 480 píxeles). Se rasteriza una vez en una máscara de 0,25° y cualquier proyección la muestrea: equirrectangular, Robinson (la de los atlas escolares) y globo ortográfico. Los mares interiores y los lagos (mar Negro, Caspio, Grandes Lagos) se restan de la tierra.

- `isLand(lon, lat)`
- `robTab(T, alat)`
- `robinson(lon, lat)`
- `robinsonInv(x, y)`
- `mapView(v)`: vista de mapa: { proj: 'eq' | 'rob', x, y, w, h (en pantalla o mundo), lon0, lon1, lat0 (sur), lat1 (norte) } devuelve funciones para pasar de geografía a píxel y al revés
- `mapGrid(v)`
- `drawMap(v, paint)`: pinta un mapa: paint(clase, x, y, lon, lat) devuelve un índice de color o -1 (no pinta)
- `geo(v, lon, lat)`
- `mapDist(v, maxD = 10)`: distancia (en píxeles, hasta maxD) de cada píxel de mar a la tierra más cercana; 0 en tierra
- `drawGlobe(cx, cy, r, lon0, lat0, paint)`: globo ortográfico centrado en (lon0, lat0); paint(clase, x, y, luz 0..1) con clase 1 mar, 2 tierra, 3 costa
- `globePt(cx, cy, r, lon0, lat0, lon, lat)`: posición en pantalla de un punto geográfico en el globo (o null si queda detrás)

### people

Personajes: bustos de 64 x 64 (Vega, Galileo, Milagros, Daniel) dibujados con primitivas y detalles de píxel, con parpadeo, cejas y bocas que siguen la envolvente de la voz; y Bit, el robot ayudante. bust(x, y, ESPECIE, { t, who, expr, look, blink }) dibuja en la caja 64 x 64 con esquina en (x, y).

- `stampMap(rows, x, y, map, flip = false)`
- `blinkAt(t, seed = 0)`: parpadeo determinista: cada 2,6 a 4,6 s, 0,12 s
- `mouthShape(m, expr)`
- `faceIn(lx, ly, S)`: ---------- cara genérica ---------- forma de la cara: óvalo con barbilla; devuelve true si (lx, ly) (coordenadas de la caja) está dentro
- `drawFace(x, y, S, st)`
- `drawShoulders(x, y, S)`: cuerpo (hombros y cuello)
- `bumpyEllipse(cx, cy, rx, ry, c, seed, amp = 1.6, freq = 9)`: ---------- peinados y complementos ----------
- `bust(x, y, S, st = {})`: busto completo con contorno; st: { t, who (para la boca), mouth, expr, look, bob }
- `bit(cx, cy, st = {})`: bit(cx, cy, { t, expr, mouth, look, color, acc }) cabeza de pantalla flotante con antena y propulsor. color y acc fuerzan un aspecto (las pantallas de la app que enseñan otro color o complemento); si no, el que se ha elegido
- `bitAccessory(cx, cy, acc, t)`: complementos de Bit (recompensas de la app): gafas, gorra, pajarita, corona, casco, medalla
- `sweatDrop(x, y, t)`: sudor de nervios (gota) junto a un personaje
- `galileoFigure(x, y, st = {})`: ---------- Galileo de cuerpo entero (toga negra de estudiante) ---------- galileoFigure(x, y, { t, pose: 'stand' | 'sit', arms: 'rest' | 'pulse' | 'point' | 'think' | 'cheer' | 'release', expr, look }) (x, y) es la esquina de la caja del busto; el cuerpo baja hasta y + 102 de pie o y + 100 sentado

### kit

---------- signos de moneda que faltan en la fuente ----------

- `bigText(str, x, y, o = {})`: ---------- textos ---------- texto grande con degradado por filas, contorno y sombra dura
- `monoText(str, x, y, o = {})`: máquina de escribir: cada letra ocupa la misma celda; n = letras visibles (fracción: la última aparece con golpe)
- `monoWidth(str, s = 1, cell = 6)`
- `handText(str, x, y, o = {})`: letra a mano: cada letra con un pequeño desplazamiento propio y renglón ondulado; upto = letras visibles
- `pathLengths(P)`: ---------- trazados ----------
- `pathPoint(P, k)`
- `drawPath(P, k, o = {})`: dibuja el trazado hasta la fracción k; o: { dash: [lleno, hueco], w (grosor), c, dots }
- `smoothPath(P, n = 8)`: curva suave por puntos de control (Catmull-Rom), n puntos por tramo
- `saggy(a, b, sag = 6, n = 16)`: hilo con catenaria entre dos puntos (cuelga un poco)
- `iconSun(x, y, r, t, o = {})`: ---------- iconos del tiempo ----------
- `iconCloud(x, y, w, o = {})`
- `iconStorm(x, y, w, t, o = {})`: tormenta: nube oscura, lluvia que cae y rayo intermitente
- `polaroid(x, y, w, h, fn, o = {})`: ---------- objetos de papel ---------- polaroid: marco blanco con zona de foto (fn dibuja dentro con recorte) y pie opcional escrito a mano
- `pinHead(x, y, c = C.hilo[2], o = {})`
- `tape(x0, y0, x1, y1, w, c, k = 0.55)`: cinta adhesiva semitransparente (tramado) de ancho w entre dos puntos
- `stampBox(str, x, y, c, o = {})`: sello de tinta: rectángulo de doble borde con texto; la tinta se «gasta» con un tramado irregular
- `stampIn(k)`: aparición de un sello: grande y transparente → golpe (k de 0 a 1)
- `zoom2(fn, ox = 0, oy = 0)`

### props

Piezas reutilizables: tarjetas de nivel y de fase, ventana de sistema, globo terráqueo, llave, gato, cazo con termómetro, ladrillos, cuaderno, reloj de péndulo, taller de Galileo y péndulos.

- `levelCard(t, lt, n, title, rmp, label = 'NIVEL')`: tarjeta de nivel a pantalla completa (lt: tiempo desde su inicio; label: 'NIVEL' en los vídeos, 'MISIÓN' dentro de un nivel de la app, donde «nivel» ya es cada punto del mapa)
- `phaseBanner(t, t0, n, title)`: rótulo grande de fase del método (colores del libro) que luego se queda pequeño arriba
- `timeCard(t, lt, str)`: cartel «UN RATO DESPUÉS...» a pantalla completa
- `osWindow(x, y, w, h, title, o = {})`: ventana de sistema operativo con barra de título
- `globe(cx, cy, r, t, o = {})`: globo terráqueo que gira (continentes con ruido)
- `keyIcon(x, y, rmp = C.yellow)`: llave dorada (las «claves»)
- `grumpyCat(cx, cy, t)`: gato de píxel con cara de pocos amigos (chiste de Bit)
- `boilingPot(cx, cy, t)`: cazo con agua hirviendo y burbujas
- `brick(x, y, w, str, rmp)`: ladrillo con texto (torre de la teoría)
- `notebook(x, y, w, h)`: hoja de cuaderno con renglones
- `pendulumClock(cx, top, t, T = 2)`: reloj de pie con péndulo (periodo T)
- `workshop(t, o = {})`: taller de Galileo: pared de madera, ventana con colinas de la Toscana, estante con libros y mesa
- `pendulumRig(x0, y0, t, t0, specs)`: soporte con tres péndulos de distintas longitudes; lens en píxeles, periodos en segundos; t0: suelta

### sets

Decorados. Cada uno dibuja su mundo con la cámara vigente (setCam) y capas con paralaje (withCam).

- `pendAngle(t, t0, T, a0)`: ---------- péndulo: física real (isocronismo con oscilaciones pequeñas) ---------- ángulo en el instante t de un péndulo de periodo T (ida y vuelta) y amplitud a0 que se suelta en t0 desde +a0
- `pendPeriod(L)`: periodo real para una longitud L en metros (g = 9,81 m/s²)
- `cathStripes(x, y, cx, cy, r0, r1, colA, colB, n = 11)`
- `cathedral(t, o = {})`
- `cathLight(t, k = 1)`: haces de luz desde las ventanas altas (tramado sobre lo pintado)
- `cathLamp(t, ang, o = {})`: lámpara de bronce de la catedral colgada de su cadena; devuelve la posición de la lámpara
- `pew(x, y, w)`: banco de madera de la catedral

### dock

Retrato de quien habla («facecam») abajo a la izquierda, como en un directo o un juego de rol. Cambia de persona con un corte de estática y se esconde si nadie habla durante un rato. Bit tiene su propio sitio al lado.

- `dockSpeaker(t, allow = DOCK_WHO, hold = 1.2)`: persona que habla (o la última que habló hace menos de hold s) entre las permitidas; las voces en directo mandan. Devuelve { who, since } (segundos desde que empezó a hablar)
- `speakerDock(t, o = {})`: o: { allow, hold, x, y, expr: { vega: 'happy' }, fixed: 'vega' (siempre esa persona), label }

### brand

Identidad de la serie: logotipo, fondo animado y tarjetas de opciones de los retos. logotipo de la serie: «MODO» en una etiqueta y «CIENCIA» grande con degradado

- `logo(cx, cy, lt, o = {})`: Identidad de la serie: logotipo, fondo animado y tarjetas de opciones de los retos. logotipo de la serie: «MODO» en una etiqueta y «CIENCIA» grande con degradado
- `seriesBg(t, o = {})`: fondo animado de la serie: rejilla violeta con iconos de ciencia que flotan (o.names: otros iconos)
- `quizCard(x, y, letter, text, o = {})`: tarjeta de opción con letra o número

### lab

Material de laboratorio y piezas de la práctica de los protectores solares (reutilizables en otras unidades). Cada instrumento se dibuja con su base en (cx, by) y devuelve su altura.

- `GLASS()`: Material de laboratorio y piezas de la práctica de los protectores solares (reutilizables en otras unidades). Cada instrumento se dibuja con su base en (cx, by) y devuelve su altura.
- `glassStroke(x0, y0, x1, y1)`
- `liquid(rmp)`
- `pipette(cx, by, o = {})`: pipeta graduada con pera de goma
- `volumetricFlask(cx, by, o = {})`: matraz aforado: bola, cuello largo con una sola raya (aforo) y tapón
- `cylinder(cx, by, o = {})`: probeta con pie y escala
- `testTube(cx, by, o = {})`: tubo de ensayo con marcas poco precisas
- `beaker(cx, by, o = {})`: vaso de precipitados con pico y escala aproximada
- `sunscreen(cx, by, label, rmp = C.orange, o = {})`: bote de protector solar con etiqueta
- `oilBottle(cx, by)`: frasco de aceite mineral y tubo de crema de óxido de cinc
- `creamTube(cx, by)`
- `sheetCircles(x, y, o = {})`
- `resultFigure(x, y, letter, o = {})`

### megauv

MEGA UV, el jefe final de la unidad 1: lo dibujan el nivel (también como cartucho) y la app (mapa y cromo). st: { t, mouth, expr: 'grin' | 'laugh' | 'hurt' | 'ko', hit (0..1 destello), alpha }

- `megaUV(cx, cy, r, st = {})`: MEGA UV, el jefe final de la unidad 1: lo dibujan el nivel (también como cartucho) y la app (mapa y cromo). st: { t, mouth, expr: 'grin' | 'laugh' | 'hurt' | 'ko', hit (0..1 destello), alpha }

### captions

Subtítulos al estilo de TikTok: una línea grande (fuente ×2), pocas palabras por página y la palabra que suena resaltada con el color de quien habla. Las páginas se calculan al cargar el episodio (determinista). Las voces en directo de la app llevan sus propias páginas, con tiempos relativos a su comienzo.

- `capPagesOf(captions)`
- `capPageAt(t, pages = CAP_PAGES)`
- `drawCapPage(p, t, y)`

### hud

Marcador de videojuego en la parte de arriba: nivel y título a la izquierda, fases del método (si toca) y la experiencia (XP) a la derecha. El episodio rellena HUD_LEVELS y EP_XP ([[t, puntos], ...]).

- `xpAt(t)`

### input

Zonas táctiles (app interactiva). Mientras se dibuja un fotograma, cada elemento que se puede tocar se apunta con hit(id, x, y, w, h); la app busca la zona bajo el dedo. Las escenas de vídeo no apuntan nada.

- `hit(id, x, y, w, h, o = {})`: coordenadas de pantalla (se descuenta la cámara); o: { data, cursor }
- `isOver(id)`
- `isPressed(id)`

### directo

EN DIRECTO: piezas del informativo (plató, presentadora, rótulos, teletipo, cabecera, cortinillas de sección, encuestas con cuenta atrás y paneles de gráficos). Todo en coordenadas de pantalla.

- `albaAt(x, y, t, st = {})`: Alba detrás de la mesa: busto con torso que baja hasta la mesa; la boca sigue su voz
- `liveBadge(x, y, t, label = 'EN DIRECTO')`: ---------- rótulos y marcas del canal ----------
- `tvClock(x, y, str)`
- `channelBug(x, y)`
- `tvCorner(t, o = {})`: esquina superior: logotipo, directo y reloj
- `ticker(t, k = 1, text = TICKER_TEXT)`: teletipo inferior (14 px): etiqueta dorada y texto que corre; k para entrar desde la izquierda
- `lowerThird(y, k, title, sub, o = {})`: rótulo inferior de dos franjas (tema y subtítulo); k de 0 (fuera) a 1
- `graphicHeader(tag, title, t, o = {})`: franja de título de las pantallas de gráficos: etiqueta de sección (color) y título
- `graphicBg(t, o = {})`: fondo de las pantallas de gráficos: degradado de plató con rejilla suave y el mapa en líneas
- `tvPanel(x, y, w, h, o = {})`: panel de gráfico con marco cromado
- `drawCabecera(t, lt, sid, band)`: ---------- cabecera del informativo ----------
- `tvGlobe(cx, cy, r, lon, lat)`: globo del canal (azules y cian, con meridianos)
- `studio(t, wallFn, o = {})`: dibuja el plató con la pantalla gigante; wallFn(x, y, w, h) pinta el contenido de la pantalla
- `sectionWipe(k, label, num, fill = RD_[2])`: cortinilla de sección: franja de color que barre la pantalla con el nombre de la sección (k de 0 a 1)
- `quizPanel(x, y, w, t, q, opts, correct, tc, tr)`: ---------- encuesta: pregunta, opciones, cuenta atrás y respuesta ---------- opts: [['A', 'Producción'], ...]; correct = índice; tc = inicio de la cuenta atrás; tr = revelación

### rumbo

RUMBO: piezas del cuaderno de viaje (mesa, cuaderno de cuero, páginas, letra a mano, mapas de acuarela con China resaltada, rutas con tren y barco, sellos y la foto de Times Square).

- `note(str, x, y, o = {})`: cursiva de cuaderno: letra inclinada con un temblor leve
- `noteW(str, s = 1)`
- `writing(t, t0, str, cps = 22)`: letras visibles al escribir a mano desde t0 (cps letras por segundo)
- `woodTable()`: ---------- mesa de madera vista desde arriba ----------
- `brassCompass(cx, cy, r, t)`: brújula de latón con la aguja que oscila
- `pencil(x0, y0, x1, y1)`
- `ticket(x, y)`
- `coin(x, y)`
- `leather(x, y, w, h, shade = 0)`
- `coverFront(t)`: tapa cerrada: cuero, pespunte, título y rosa de los vientos en oro
- `strap(t, t0)`
- `paperPage(x, y, w, h, spineSide)`: página de papel con textura, sombra hacia el lomo y cantos de las hojas
- `titlePage(x, y, t, t0)`: título de la primera página, escrito a mano
- `endpaper(x, y, w, h)`: guarda del interior de la tapa: mapa antiguo en tono pergamino
- `spread()`: ---------- doble página del cuaderno (escenas 1 y 2) ----------
- `spineShade()`
- `robFit(x, y, w, h, lon0, lon1, latTop)`: ---------- escena 1: mapa de la Nueva Ruta de la Seda (mapa 8 del libro) ---------- vista Robinson que conserva la proporción: se busca la latitud sur que encaja en el alto pedido
- `inPoly(lon, lat, P)`
- `chinaMask(V)`
- `watercolorMap(V, o = {})`
- `routePts(list, smooth, V = SILK_V)`
- `labelFractions(list, V = SILK_V)`: fracción del trazado en la que queda cada punto rotulado
- `cityLabel(q, t, tShow)`
- `vehicleAt(P, k, spr, dirFlip)`
- `spr_(s, x, y, flip)`
- `compassRose(cx, cy)`
- `timesSquare(x, y, w, h, t)`: ---------- escena 2: Nueva York y las dos crisis (situación de aprendizaje, pág. 52) ----------
- `virusDoodle(cx, cy, r, c)`
- `noraSelfie(x, y, t, o = {})`: foto de Nora pegada en el cuaderno: la boca sigue su voz

### iconos

Iconos pixelados de economía y sociedad para los gráficos (unos 24 px, centrados en cx, cy, con contorno oscuro). picto(nombre, cx, cy, { t, s (1 o 2: escala entera), ol }) dibuja el icono; PICTOS lista los disponibles.

- `picto(name, cx, cy, o = {})`

### personajes

Personajes del informativo que salen en varios vídeos. Nora, la enviada especial: melena ondulada caoba, americana azul y acreditación de prensa.

- `bgBakery(t)`: fondos de la calle (a 240 × 135, para el plano cercano ×2)
- `bgTownHall(t)`
- `bgStreet(t)`
- `micro(x, y, t)`

### remix

REMIX, la serie de Música (historia de la música): su identidad, que comparten sus cartuchos y el menú de la app.   · Lía hace música en su cuarto y REC, su casete parlante, rebobina hasta cada época para grabar sus sonidos   · el presente: el cuarto-estudio de noche (neones, espuma acústica, altavoces que laten con el bombo)   · el pasado: cada época con su arte; la Edad Media, como una página de códice (pergamino, oro, celosía roja y azul,     arcos románicos), igual que las Cantigas de Santa María   · los retos: pads de un sampler que se iluminan al tocarlos La propuso el agente externo con su prueba de ambiente (m01-ambiente, 1/10/2026) y pasó al motor al revisarla. El pulso de la música sale del tempo de cada canción de la receta (TL.music trae bpm): así todo baila a tiempo.

- `bpmOf(song, def)`
- `beatK(t, t0, bpm, d = 0.18, sub = 1)`: golpe de pulso (1 justo en el pulso, cae en d segundos) de una música que empezó en t0
- `beatN(t, t0, bpm)`
- `lia(x, y, t, o = {})`
- `rec(cx, cy, st = {})`: ---------- REC, la casete ---------- carcasa de casete con etiqueta de rayas; las dos bobinas son los ojos (giran: hacia atrás al rebobinar) y la boca está en la ventana de la cabeza lectora. st: { t, spin (vueltas por segundo, negativo hacia atrás), expr, rec, s }
- `remixLogo(cx, cy, o = {})`: ---------- logotipo ---------- REMIX con neón: rosa a violeta, contorno oscuro y doble sombra (cian y rosa) como un vídeo musical; k: pulso
- `remixCodex(cx, cy, n = 5)`: la misma palabra en un códice: inicial iluminada (R de oro sobre azul con marco rojo) y el resto en tinta
- `codexInitial(ch, x, y, sz)`: letra capital de códice: cuadrado azul con zarcillos blancos, letra de oro y marco rojo
- `remixStudio(t, o = {})`: ---------- el presente: el cuarto-estudio de Lía ---------- m: { t0 (cuándo empezó la música), notes: notas que suenan ahora (teclas encendidas) }
- `tapeRewind(t, k)`: ---------- el viaje en el tiempo ---------- rebobinado de vídeo: líneas que se desplazan, franjas de ruido y color que vira hacia el sepia. k: 0..1
- `tapeOsd(x, y, label, t)`: rótulo del aparato de vídeo: ◀◀ y texto en letra de pantalla
- `codexPage(t)`
- `codexLattice(x, y, w, h)`: celosía de las miniaturas: rombos rojos y azules con puntos de oro
- `codexArcade(x, y, w, h, n)`: arcada románica: columnas con capitel y arcos de medio punto (de piedra con dovelas)
- `codexFigure(x, y, o = {})`: figura sentada al estilo de las miniaturas: túnica, calzas, zapatos de punta, cara sencilla. o: { tunic, hose, hair, hat, flip, nod (cabeceo 0..1) }
- `zanfonaProp(cx, cy, ang, o = {})`: zanfona: caja con forma de violín, teclado arriba y manivela que gira. cx, cy: centro; ang: giro de la manivela
- `laudProp(cx, cy, o = {})`: laúd: caja de pera, mástil y clavijero doblado hacia atrás
- `codexMiniature(t, x, y, w, h, o = {})`: la miniatura de la Edad Media: juglar con zanfona y trovador con laúd bajo una arcada, sobre la celosía
- `noteGlyph(x, y, c)`: corchea de píxel
- `samplerPads(S, labels, o = {})`
- `samplerLcd(x, y, w, h, text, o = {})`: pantalla de cristal líquido del sampler con la pregunta
- `remixNeonBg(t, t0, bpm, o = {})`: ---------- fondo del presente: cielo de neón, sol a rayas, rascacielos que son un ecualizador y suelo de rejilla ----------
- `remixNotes(t, n = 10, cols = [C.pink[3], C.tvCyan[1], C.yellow[3]])`: corcheas que flotan
- `scratchShift(dx)`: sacudida horizontal de toda la imagen (la rayada del vinilo)
- `remixStudioNotes(t, t0)`
- `liaAtDesk(x, y, t, o = {})`

### main

Renderizador: cada fotograma es una función pura del tiempo. Dibuja la escena vigente (y la anterior durante la transición de entrada), encima el marcador (HUD) y los subtítulos, y convierte el búfer indexado a RGBA 480 x 270.

- `frameFx(o)`

### ix-kit

Piezas de los retos: preguntas de opción múltiple con corrección hasta acertar, tarjetas grandes para el dedo y botones. Tamaños pensados para el móvil en horizontal: 30 píxeles de arte de alto son unos 44 puntos.

- `choiceIX(c)`: Reto de opción múltiple. c: { n, correct, wrong: { índice: clip }, hint: clip, xp: [30, 15, 5], label, shuffle, onRight(S, X, first), onWrong(S, X, i), done (segundos hasta cerrar el reto al acertar: 0,4) }. Cada fallo explica por qué esa opción no vale y la deja apagada; tras dos fallos llega una pista. La respuesta buena da menos puntos cuantos más intentos haya costado.
- `clipText(id)`: texto de una voz suelta (para dejar la pista escrita en pantalla)
- `drawChoice(S, labels, o = {})`: tarjetas de respuesta. S: estado del reto (o null antes de abrirlo); labels: textos en el orden original. o: { x, y, w, h, gap, cols, colW, t0 y now (aparición: en las escenas, tiempo de la historia), badge: (k) => '1' }
- `orderIX(c)`: Reto de ordenar: se tocan las tarjetas en el orden elegido; cada una recibe su número y tocar una numerada la quita (las siguientes suben un puesto). c: { n, correct: índices en su orden, check: corregir al pulsar LISTO (si no, la respuesta solo se guarda para comprobarla más tarde), auto: se corrige al tocar la última (sin LISTO), wrong: clip si el orden no es el bueno (o wrongFor(respuesta) → clip, con sus clips en wrongClips), xp, label, onSaved }
- `drawOrder(S, labels, o)`: tarjetas del reto de ordenar. o: { pos: [[x, y, w?], ...] (una por tarjeta), letters, ok: [x, y, w, h], clear: [...] }
- `binIX(c)`: Reto de clasificar: frases de una en una, cada una en su caja (hecho u opinión, sector primario, secundario o terciario...). c: { items: caja correcta de cada frase, wrong: clip (o { frase: clip }), xp (por acierto), label, quiet: sin voz al acertar (con muchas frases, solo suena el efecto), cool: segundos sin tocar tras cada respuesta (se ve cuál era la buena), onEnd(S, X, fallos) }. Si se falla, se explica y se pasa a la siguiente.
- `drawBins(S, bins, o)`: cajas del reto de clasificar. bins: [[texto, subtítulo?, rampa], ...]; o: { x, y, w, h, gap, answer: caja buena de la frase actual (para colorear tras tocar) }
- `drawHint(S, id, x = 92, y = 214, w = 296)`: pista escrita (tras dos fallos)
- `button(id, x, y, w, h, label, o = {})`: botón de la app. o: { fill, ramp, icon, s (tamaño de letra), disabled, c }
- `stars(cx, cy, n, o = {})`: estrellas de nivel (0..3); k: animación de cada una (array) o null

### ix-directo

Retos con la estética de EN DIRECTO (los usan todos los niveles de Geografía e Historia): encuesta que se contesta tocando, tarjetas para ordenar y cajas para clasificar. La lógica es la del kit de la app (choiceIX, orderIX, binIX en 25-ix-kit.js); aquí se dibuja y se apuntan las zonas que se pueden tocar. Todo lo que se toca también funciona un poco antes de abrir el reto (salta el resto de la voz).

- `tvOpen(S)`: Retos con la estética de EN DIRECTO (los usan todos los niveles de Geografía e Historia): encuesta que se contesta tocando, tarjetas para ordenar y cajas para clasificar. La lógica es la del kit de la app (choiceIX, orderIX, binIX en 25-ix-kit.js); aquí se dibuja y se apuntan las zonas que se pueden tocar. Todo lo que se toca también funciona un poco antes de abrir el reto (salta el resto de la voz).
- `shakeX(S, tw)`
- `tvCheck(cx, cy, c)`
- `tvCross(cx, cy, c)`
- `tvHover(id, i)`
- `tvPoll(x, y, w, t, q, opts, S, o = {})`: ---------- encuesta: cabecera roja, pregunta y filas A, B, C, D ---------- S: estado del reto (choiceIX); o: { t0 (entrada de las filas), hint (id de la pista), hx, hy, hw (dónde se escribe) }
- `tvOrderCards(S, cards, slots, t, o = {})`: ---------- tarjetas para ordenar: cada una recibe su número al tocarla y se puede quitar tocándola otra vez ---------- cards: [[título, picto, color], ...] en su orden bueno; slots: x del centro de cada tarjeta en pantalla y qué tarjeta va en cada hueco ([[x, índice], ...]); o: { y, w, h, t0 }
- `tvBins(S, bins, items, good, t, o = {})`: ---------- cajas para clasificar ---------- bins: [{ title ('|' parte el renglón), tx (desplazamiento del título), sub, fill, x, y, w, h, s (letra del título), head (alto de la franja), subY, draw(x, y, w, h) (dibujo propio), cols, gx, ix, iy (rejilla de lo clasificado) }]; items: [[texto, picto], ...] con su caja buena en good[i]. Tras cada respuesta, la caja elegida se ilumina en verde o en rojo (y la buena, en verde) y lo ya clasificado se queda dentro.
- `tvItemCard(x, y, w, S, items, t, o = {})`: la frase que toca clasificar, con su dibujo y cuántas quedan

## Sonido: el chip de las recetas

Lo que puede usar el código de `receta/`, además de `defSong` y `defSound` (ver «La receta» en `API.md`). Todo trabaja a `SR` muestras por segundo (24.000) y las frecuencias van en hercios: `hz('A4')` da 440. Los instrumentos devuelven una señal mono que se coloca con `addMono(búfer, señal, segundo, ganancia, panorama)`; `vel` es la fuerza (1, normal).

### Piezas del sintetizador

Osciladores, envolventes, filtros, eco y sala. Las señales son Float32Array mono a SR muestras por segundo; un búfer estéreo es [L, R]. Los filtros trabajan en el sitio y devuelven la misma señal.

- `midiHz(n)`: número MIDI a hercios (69 es el la de 440 Hz)
- `noteNum(s)`: 'C4', 'F#3', 'Bb2' → número MIDI
- `stereo(n)`: búfer estéreo vacío de n muestras: [L, R]
- `addMono(dst, src, at, gain = 1, pan = 0)`: suma una señal mono en un búfer estéreo con ganancia y panorama (-1..1)
- `addStereo(dst, src, at, gain = 1)`: suma un búfer estéreo en otro a partir del segundo at
- `pulse(dur, fOf, duty = 0.5, envOf = () => 1)`: pulso de ancho duty con frecuencia variable f(t) (Hz), duración en s
- `tri(dur, fOf, envOf = () => 1, steps = 16)`: triángulo escalonado (16 niveles) como el canal de bajo de las consolas de 8 bits
- `sine(dur, fOf, envOf = () => 1)`: seno (sonido puro) de dur segundos; fOf(t) da la frecuencia y envOf(t), el volumen
- `noise(dur, rate, envOf = () => 1, short = false, seed = 1)`: ruido de registro de desplazamiento (LFSR de 15 bits); rate: veces por segundo que cambia; short: modo metálico
- `adsr(a, d, s, r, len)`: ADSR: a, d en s; s nivel 0..1; r en s; len: duración de la nota antes de soltarla
- `expDecay(tau, a = 0.002)`: envolvente de golpe: sube en a segundos y se apaga con la constante tau (percusión, campanas)
- `lowpass(x, fc)`: paso bajo suave, en el sitio: oscurece por encima de fc Hz
- `highpass(x, fc)`: paso alto suave, en el sitio: quita por debajo de fc Hz
- `biquad(x, type, f0, Q = 0.707, gainDb = 0)`: filtro de dos polos, en el sitio: 'lp' (paso bajo), 'hp' (paso alto), 'bp' (paso banda) o 'peak' (realza o recorta gainDb en f0)
- `pingPong(buf, time, fb = 0.3, mix = 0.25)`: eco estéreo (ping-pong) sobre un búfer estéreo
- `reverb(buf, size = 0.8, damp = 0.3, mix = 0.25)`: reverberación de Schroeder sencilla (cuatro peines y dos pasatodo por canal)
- `softClip(x, drive = 1)`: saturación suave: redondea los picos y calienta el sonido
- `fade(x, fin, fout)`: fundido de entrada y de salida, en segundos
- `concat(...parts)`: pega señales una detrás de otra
- `mixInto(dst, src, at = 0, g = 1)`: suma una señal mono en otra a partir del segundo at, con ganancia g

### Chiptune de MODO CIENCIA

Las voces de 8 bits de la música de MODO CIENCIA, en el objeto chip.

- `chip.lead(dur, f, vel = 1, o = {})`: melodía: pulso con un vibrato leve que entra tras el ataque. o: { duty } (0.125, 0.25 o 0.5: de fino a hueco)
- `chip.arp(dur, f, vel = 1, o = {})`: arpegio: pulso fino y corto. o: { duty }
- `chip.bass(dur, f, vel = 1)`: bajo: triángulo escalonado de las consolas de 8 bits
- `chip.pad(dur, f, vel = 1)`: colchón: pulso filtrado con ataque lento
- `chip.kick(vel = 1)`: bombo
- `chip.snare(vel = 1)`: caja de ruido
- `chip.hat(vel = 1)`: charles cerrado
- `chip.openHat(vel = 1)`: charles abierto

### Instrumentos de EN DIRECTO

FM, cuerda pulsada, láminas, soplo, cuerdas, timbales y baterías (en el objeto drums).

- `white(n, seed = 1)`: ruido blanco determinista
- `saw(dur, fOf, envOf = () => 1, phase0 = 0)`: diente de sierra con antialias (PolyBLEP)
- `fm(dur, f, o = {})`: FM de dos operadores, la base de metales, pianos eléctricos y campanas. o: { ratio, index (número o función del tiempo), env (función del tiempo), vib, fb }
- `brass(dur, f, vel = 1)`: metal de sintetizador de los ochenta (FM con el índice que se abre al atacar)
- `epiano(dur, f, vel = 1)`: piano eléctrico (lengüeta FM: golpe brillante que se apaga y cuerpo suave)
- `fmBell(dur, f, vel = 1)`: campana FM (sintonías, avisos)
- `synthBass(dur, f, vel = 1)`: bajo de sintetizador (FM con pellizco)
- `strings(dur, f, vel = 1, att = 0.35)`: cuerdas: varios dientes de sierra desafinados, filtrados, con ataque lento
- `pluck(dur, f, o = {})`: cuerda pulsada (Karplus-Strong), la base de guitarra, laúd y salterio. o: { damp (0.990..0.999), bright (0..1 excitación), pick (posición 0..0.5), body }
- `guitar(dur, f, vel = 1, o = {})`: guitarra española (nailon): cuerda pulsada oscura con resonancia de caja
- `upright(dur, f, vel = 1)`: contrabajo pulsado (jazz)
- `marimba(dur, f, vel = 1)`: marimba: láminas de madera, golpe cálido
- `vibes(dur, f, vel = 1, o = {})`: vibráfono: láminas de metal con trémolo. o: { trem (Hz, 5,2), len (lo que suena antes de apagarse) }
- `glock(dur, f, vel = 1)`: glockenspiel: láminas de metal pequeñas, muy brillantes
- `flute(dur, f, vel = 1)`: flauta travesera, con vibrato
- `mutedTrumpet(dur, f, vel = 1)`: trompeta con sordina harmon: diente de sierra con formantes nasales
- `timpani(dur, f, vel = 1)`: timbal de orquesta
- `orchHit(notes, vel = 1)`: golpe de orquesta (sintonías): metales + cuerdas + timbal + ruido, corto
- `drums.kick(vel = 1)`: bombo
- `drums.kickSoft(vel = 1)`: bombo suave
- `drums.snare80(vel = 1)`: caja de los ochenta con «reverberación cortada»
- `drums.tom(f = 110, vel = 1)`: tom; f, su altura en Hz
- `drums.hat(vel = 1, open = false)`: charles; open, abierto
- `drums.clap(vel = 1)`: palmada
- `drums.ride(vel = 1)`: plato ride (jazz)
- `drums.brushTap(vel = 1)`: escobilla: golpe
- `drums.brushSwish(dur = 0.5, vel = 1)`: escobilla: barrido de dur segundos
- `drums.shaker(vel = 1)`: maraca (percusión de viaje)
- `drums.cajonLow(vel = 1)`: cajón: golpe grave
- `drums.cajonSlap(vel = 1)`: cajón: golpe seco
- `drums.tambourine(vel = 1)`: pandereta

### Edad Media

Zanfona, flauta de pico, laúd, salterio, percusión de mano (en el objeto mano), nácaras, campana y coro gregoriano.

- `wobble(n, rate, seed = 1)`: ruido suave: valores al azar (deterministas) unidos con curvas, rate veces por segundo; de -1 a 1
- `zanfona(dur, f, vel = 1, o = {})`: cuerda frotada: diente de sierra con el temblor de la rueda (afinación y resina), formantes de la caja y un poco de roce. o: { att, drone (bordón: más oscuro), seed }
- `zanfonaGolpe(dur, f, vel = 1)`: golpe de la trompeta (el «perro» que vibra contra la tapa cuando se acelera la rueda): zumbido corto y áspero
- `flautaPico(dur, f, vel = 1, o = {})`: soplo casi sin vibrato, con el «chiff» del ataque y una pequeña subida de afinación al empezar
- `laud(dur, f, vel = 1, o = {})`: laúd: dos cuerdas por orden (unísono algo desafinado), púa, caja de madera
- `laudRasgueo(dur, freqs, vel = 1, o = {})`: rasgueo de laúd: las cuerdas de abajo arriba con un poco de separación
- `salterio(dur, f, vel = 1, o = {})`: salterio: cuerdas de metal muy brillantes que suenan mucho rato
- `mano.dum(vel = 1)`: darbuka: «dum» grave en el centro del parche, «tek» seco en el borde y «ka» suave con la otra mano
- `mano.tek(vel = 1)`: darbuka: «tek», seco en el borde del parche
- `mano.ka(vel = 1)`: darbuka: «ka», suave con la otra mano
- `mano.pandero(vel = 1)`: pandero: golpe grave de parche grande
- `mano.sonajas(vel = 1)`: sonajas (pandereta)
- `mano.madera(vel = 1, f = 1150)`: bloque de madera (el «reloj» de los retos)
- `nacara(f = 190, vel = 1)`: nácaras: dos timbales pequeños de origen árabe
- `campana(dur, f, vel = 1)`: campana de iglesia: parciales de campana (zumbido, fundamental, tercera menor, quinta y nominal) con su batido
- `coro(dur, f, vel = 1, o = {})`: coro gregoriano: voces de hombre al unísono, cada una con su vibrato, cantando una vocal. o: { vowel (a, o, u o e), voices (5), att, rel }

### Estudio de hoy

Piano eléctrico, bajo sub, batería lo-fi (en el objeto lofi), vinilo y lectura a velocidad variable.

- `rhodes(dur, f, vel = 1)`: piano eléctrico de grabación vieja: dos lengüetas casi iguales y el brillo recortado
- `subBass(dur, f, vel = 1)`: bajo sub: seno con un poco de segundo armónico (para que se oiga en el altavoz del móvil)
- `lofi`: batería polvorienta (lo-fi): bombo redondo, caja apagada, aro, platos sin brillo
- `lofi.kick(vel = 1)`: bombo redondo
- `lofi.snare(vel = 1)`: caja apagada
- `lofi.rim(vel = 1)`: aro
- `lofi.hat(vel = 1, open = false)`: charles sin brillo; open, abierto
- `lofi.shaker(vel = 1)`: maraca
- `lofi.reverse(dur = 1.2, vel = 1)`: platillo al revés (sube hasta el golpe)
- `vinilo(dur, seed = 3, density = 9)`: crujido de disco: chasquidos sueltos, algún estallido y un siseo muy bajo
- `varispeed(src, dur, pos)`: lectura de una muestra a velocidad variable (para rebobinar o rayar): pos(t) da el segundo de la muestra que suena en t

### Ayudas para escribir canciones

Notas, acordes, reloj de compases, melodías en texto, azar que se repite y acabado de una mezcla.

- `hz(s)`: nota a frecuencia: hz('A4') es 440
- `chord(s)`: acorde a frecuencias: chord('D3 F3 A3')
- `bus(sec)`: búfer estéreo vacío de sec segundos (un bus para mezclar por grupos)
- `clock(bpm, o = {})`: reloj de compases: C.T(compás, paso) da el segundo; C.st es un paso y C.bar un compás. o: { steps (pasos por compás: 16 en 4/4, 12 en 6/8), beat (pasos por pulso: 4, o 6 en 6/8 con bpm en negras con puntillo), swing8, swing16 }
- `melody(s, u = 1)`: melodía en ranuras, un compás entre barras: melody('A4 . D5 E5 | F5 - E5 .', 2) da, por compás, notas { n, s, l } (nota, paso y pasos que dura). Cada ranura dura u pasos; '.' alarga la nota anterior y '-' es silencio.
- `rand(seed = 1)`: azar que se repite: const r = rand(7); r() da números de 0 a 1, siempre los mismos con la misma semilla
- `toMono(b)`: mezcla estéreo en mono
- `mixdown(out, o = {})`: acabado de una mezcla, en el sitio: quita los graves que tapan la voz, eco y sala. o: { hp (Hz, 60), lowCut (false: sin el recorte de 200 Hz), lp (Hz), echo (segundos), echoMix, room (0.7), rev (0.16; 0, sin sala) }
