Guía global · el making-ofGlobal guide · the making-of
Diez homepages radicalmente distintas de CyberSenseAI + una puerta de entrada que sintetiza webs en tiempo real, construidas de forma autónoma por Claude Fable 5: workers en paralelo, capturas reales con Playwright, tres pasadas de crítica obligatoria por sitio y deploy en Hetzner. Esta guía documenta el workflow completo para que cualquiera pueda replicarlo. Ten radically different CyberSenseAI homepages + a gateway that synthesizes websites in real time, built autonomously by Claude Fable 5: parallel workers, real Playwright screenshots, three mandatory critique passes per site and deployment on Hetzner. This guide documents the full workflow so anyone can replicate it.
El pipeline de un vistazoThe pipeline at a glance
La única intervención humana es la primera: fijar los conceptos. Todo lo demás — código, crítica, iteración y verificación — lo ejecutan instancias de Claude Fable 5 en CLI headless. The only human intervention is the first stage: locking the concepts. Everything else — code, critique, iteration and verification — is executed by headless CLI instances of Claude Fable 5.
01
Antes de lanzar nada, se fija por escrito un concepto radical por sitio — no «diez variaciones de una plantilla», sino diez universos con una técnica central propia que debe deslumbrar. Cada concepto se convierte en un prompt de build (prompts/<slug>.build.md) con el contenido real de CyberSenseAI, las reglas (single-file, sin librerías, sin lorem, sin errores de consola) y la exigencia de una guía /guide por sitio.
Before launching anything, a radical concept per site is locked in writing — not "ten variations of a template" but ten universes, each with a signature technique that must dazzle. Each concept becomes a build prompt (prompts/<slug>.build.md) with the real CyberSenseAI content, the rules (single-file, no libraries, no lorem, no console errors) and the requirement of a per-site /guide.
| slug | ConceptoConcept | Técnica claveKey technique |
|---|---|---|
centinela | Sala de operaciones de ciberseguridadCybersecurity ops room | Radar de perímetro WebGLWebGL perimeter radar |
organigrama | El organigrama vivoThe living org chart | Grafo físico interactivoInteractive physics graph |
xaios | El sistema operativoThe operating system | Boot de terminal → diagrama 3DTerminal boot → 3D diagram |
fabrica | Línea de montaje del pensamientoAssembly line of thought | Cinta 3D isométricaIsometric 3D conveyor |
quirofano | Salud: precisión que respiraHealth: precision that breathes | ECG que dibuja la páginaAn ECG draws the page |
parquet | El parqué financieroThe trading floor | Ciudad de velas 3D + odómetros ROI3D candlestick city + ROI odometers |
colmena | Equipos híbridos: la colmenaHybrid teams: the hive | Retícula hexagonal vivaLiving hexagonal lattice |
adn | Los valores CREI en el ADNCREI values in the DNA | Doble hélice 3D3D double helix |
espejo | Humano ↔ IA en espejoHuman ↔ AI mirrored | Pantalla partida coreografiadaChoreographed split screen |
ajedrez | Estrategia: el mismo bandoStrategy: the same side | Tablero 3D humano+IA aliados3D board, human+AI allied |
02
Cada sitio lo construye un worker independiente: una invocación claude -p sin interfaz, con permisos de herramientas acotados y reintentos automáticos. Todos los workers se lanzan a la vez — el wall-clock del proyecto es el del sitio más lento, no la suma. Este es el runner real:
Each site is built by an independent worker: a headless claude -p invocation with a scoped toolset and automatic retries. All workers launch at once — the project’s wall-clock is the slowest site’s, not the sum. This is the actual runner:
# tools/worker.sh <slug> <phase> <promptfile>
for attempt in $(seq 1 12); do
timeout 3000 claude -p "$(cat "$PROMPTFILE")" \
--model claude-fable-5 --effort high \
--allowedTools "Read,Write,Edit,Bash,Glob,Grep" \
--dangerously-skip-permissions >> "$LOG" 2>&1
rc=$?
if [ $rc -eq 0 ]; then touch "$DONE"; exit 0; fi
# límite de sesión → dormir hasta el reset; glitch → reintento inmediato
if tail -3 "$LOG" | grep -qi "session limit"; then sleep "$WAIT"; else sleep 5; fi
done
Claves del diseño: cada worker deja un .done idempotente (relanzar no repite trabajo hecho), el log por sitio permite auditar cada intento, y la espera por «session limit» distingue cuota real de glitches transitorios, que se reintentan sin bajar el paralelismo.
Design keys: each worker leaves an idempotent .done marker (relaunching never repeats finished work), the per-site log makes every attempt auditable, and the "session limit" wait distinguishes real quota from transient glitches, which are retried without ever lowering parallelism.
03
Las imágenes de los diez sitios (héroes y texturas) se generan con gpt-image-2 a través de un proxy LiteLLM local con API compatible OpenAI. Los workers piden sus propios assets desde el prompt de build y los convierten a .webp. La puerta de entrada, en cambio, no usa ni una sola imagen: toda su presentación es generativa (canvas + CSS desde las paletas de mood).
The ten sites’ images (heroes and textures) are generated with gpt-image-2 through a local LiteLLM proxy exposing an OpenAI-compatible API. Workers request their own assets from the build prompt and convert them to .webp. The gateway, by contrast, uses no images at all: its whole presentation is generative (canvas + CSS derived from the mood palettes).
curl -s http://localhost:4000/v1/images/generations \
-H "Authorization: Bearer $LITELLM_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","prompt":"…dirección de arte del sitio…","size":"1536x1024"}' \
| jq -r '.data[0].b64_json' | base64 -d > hero.png
cwebp -q 82 hero.png -o hero.webp
04
Ningún sitio se queda con su primer borrador. Un pipeline por sitio (tools/pipeline.sh) encadena tres pasadas; antes de cada una, tools/shoot.py abre la página con Playwright y guarda cuatro capturas reales (hero 1440px, mitad de scroll, footer y móvil 390px) además de los errores de consola. El worker de la pasada está obligado a mirar las capturas con su herramienta de lectura de imágenes y a criticar un mínimo de 8 defectos antes de tocar código. Cada pasada tiene un foco distinto — este es el foco real de cada una:
No site keeps its first draft. A per-site pipeline (tools/pipeline.sh) chains three passes; before each one, tools/shoot.py opens the page with Playwright and saves four real screenshots (1440px hero, mid-scroll, footer and 390px mobile) plus any console errors. The pass worker is required to look at the screenshots with its image-reading tool and to critique at least 8 defects before touching code. Each pass has a different focus — this is the real focus of each:
# tools/passprompt.py — el foco de cada pasada (extracto real)
FOCUS = {
1: "PRIMERA PASADA — problemas estructurales y de concepto: ¿la técnica central
deslumbra de verdad o se queda a medias? ¿el hero para el scroll de golpe?…",
2: "SEGUNDA PASADA — craft fino: espaciados y ritmo vertical exactos, contraste AA,
estados hover/focus de TODO lo interactivo, timing/easing de cada animación…",
3: "TERCERA PASADA — la lupa final: móvil 390px impecable, 60fps, accesibilidad,
robustez (resize, WebGL no disponible), microcopy pulido, la guía /guide al día…",
}
Si la captura detecta errores de consola, el prompt de la pasada los marca como prioridad absoluta. La pasada 3 termina con una captura final de verificación. If the screenshot run detects console errors, the pass prompt flags them as top priority. Pass 3 ends with a final verification screenshot.
resize en cada cambio de dirección de scroll; el campo canvas conserva sus teselas vivas y solo crea las que faltan, redibujando en el mismo frame — nunca un flash ni un hueco en blanco.Mobile resize must never reseed the background: the URL bar fires resize on every scroll direction change; the canvas field keeps its living tiles and only creates the missing ones, redrawing in the same frame — never a flash, never a blank gap.display:none en viewports estrechos), y el campo pausa su rAF cuando la pestaña se oculta.Zero invisible work: the hero specimen never re-themes while hidden (background tab, or display:none on narrow viewports), and the field pauses its rAF when the tab hides.synth.js no llega en ~4 s (red bloqueada, offline), el botón de síntesis cae al matching de moods en vez de reintentar en silencio para siempre.Bounded waits, graceful degradation: if synth.js hasn't arrived within ~4 s (blocked network, offline), the synthesis button falls back to mood matching instead of silently retrying forever.tech_en en moods.json) — un visitante EN no debe encontrar ni una palabra suelta en español, ni siquiera dentro de una maqueta de 90 píxeles.Bilingual down to the decorative: each tile's miniature headline ships an English version (tech_en in moods.json) — an EN visitor should never meet a stray Spanish word, not even inside a 90-pixel mock.moods.json llega y el grid se reconstruye, el foco vuelve a la misma tile — tabular nunca «se cae» de la página.Keyboard focus survives re-renders: when moods.json arrives and the grid rebuilds, focus returns to the same tile — tabbing never "falls off" the page.05
La portada convierte los diez sitios en un solo producto: una web por mood. Tres mecanismos, todos client-side sobre un hosting 100% estático: The gateway turns the ten sites into a single product: one web per mood. Three mechanisms, all client-side on 100% static hosting:
/_infinite/moods.json): nombre ES/EN, vibe, keywords y paleta por sitio. Alimentan el grid, el matching difuso y el campo generativo del fondo.
Mood metadata (/_infinite/moods.json): ES/EN name, vibe, keywords and palette per site. It feeds the grid, the fuzzy matching and the generative background field.ii_mood (365 días): quien ya eligió entra directo a su web; /?choose=1 reabre el selector. El valor custom:<seed> identifica un diseño sintetizado.
Cookie ii_mood (365 days): returning visitors go straight to their web; /?choose=1 reopens the selector. The value custom:<seed> identifies a synthesized design./_infinite/synth.js): del texto libre deriva, determinísticamente desde una semilla, un sistema de diseño completo y monta la home entera al instante.
Synthesis engine (/_infinite/synth.js): from free text it deterministically derives, from a seed, a complete design system and assembles the whole home instantly.__II_FIELD_PULSE(paleta, x, y) — que propaga su paleta por el campo de fondo como una onda centrada en el propio espécimen (solo mientras está en viewport); al cargar, un pulso de bienvenida siembra el campo desde el héroe justo cuando termina la entrada orquestada; sobrevolar una tile del selector derrama su paleta en el campo desde el cursor (con throttle de 500 ms); y, de forma ambiental, cada ~8 s un mood aleatorio emite su propia onda autónoma en un punto aleatorio, para que el campo siga actuando más allá del hero y del cursor. El pulso solo reasigna objetivos de color de las teselas alcanzadas — la interpolación por frame ya existente hace el resto — así que cuesta cero frames y respeta prefers-reduced-motion. Una capa de grano SVG estático sobre el velo unifica todas las superficies bajo una misma «luz».
Layers orchestrated by pulses: every time the specimen changes mood it emits a pulse — __II_FIELD_PULSE(palette, x, y) — that propagates its palette through the background field as a wave centred on the specimen itself (only while it is in the viewport); on load, a welcome pulse seeds the field from the hero right as the orchestrated entrance settles; hovering a selector tile spills its palette into the field from the cursor (throttled to 500 ms); and, ambiently, every ~8 s a random mood emits its own autonomous wave at a random point, so the field keeps performing beyond the hero and the cursor. The pulse merely reassigns colour targets for the reached tiles — the existing per-frame interpolation does the rest — so it costs zero frames and respects prefers-reduced-motion. A static SVG grain layer over the veil unifies every surface under one "light".prefers-reduced-motion se observa en vivo (activarlo a mitad de sesión congela el campo en un mosaico estático al instante, sin recargar); los pulsos también se emiten con el foco de teclado sobre las tiles y al revelarse la propuesta de mood; en táctil, la estela de color se apaga al levantar el dedo; y el título del documento cambia de idioma con el toggle ES/EN.
Final magnifying glass (pass 3): the gateway degrades gracefully — if the synthesis engine never loads, the overlay bows out after ~4 s and the request falls back to fuzzy matching; prefers-reduced-motion is observed live (toggling it mid-session freezes the field into a static mosaic instantly, no reload); pulses also fire on keyboard focus over the tiles and when the mood verdict is revealed; on touch, the colour wake fades once the finger lifts; and the document title switches language with the ES/EN toggle.og.png (1200×630) se dibuja con un script PIL que reproduce el mismo campo de teselas y la palabra «Infinito» en degradado espectral, letra a letra — ni siquiera la imagen OG usa una foto.
Locally generated social card: the og.png (1200×630) is drawn by a PIL script reproducing the same tile field plus the word "Infinito" in a spectrum gradient, letter by letter — not even the OG image uses a photo.// synth.js — el corazón de la derivación (extracto real)
var seed = makeSeed(text, Date.now()); // hash(texto + timestamp)
var rng = mulberry32(xmur3(seed)()); // PRNG determinista
var t = traitsOf(text); // oscuro/cálido/minimal/brutal… + hue
var bg = dark ? {h:hue,s:satBase,l:6+rng()*6} : {h:hue,s:…,l:94+rng()*3};
var ink = ensureContrast({…}, bg, 8); // empuja L hasta cumplir WCAG
var accent = ensureContrast({h:hue2,…}, bg, 3.1);
var pair = PAIRS[best]; // 14 pares de Google Fonts, por traits
var layout = t.brutal ? 'brutal' : …; // editorial | poster | split | brutal
La misma semilla + el mismo texto producen siempre el mismo diseño: al volver a la portada, la cookie custom:<seed> re-sintetiza la web idéntica sin servidor ni base de datos.
The same seed + the same text always produce the same design: on return, the custom:<seed> cookie re-synthesizes the identical web with no server and no database.
06
La píldora «Quiero cambiar mi estilo de web» vive en /_infinite/widget.js: cero dependencias, Shadow DOM (no pelea con el CSS anfitrión), bilingüe por navigator.language y respetuosa con prefers-reduced-motion. Los diez index.html no se tocan: la inyección la hace nginx en el momento de servir, y el conjunto se publica en un subdominio nuevo con TLS, sin rozar el dominio corporativo vivo:
The "Change my web style" pill lives in /_infinite/widget.js: zero dependencies, Shadow DOM (never fights the host CSS), bilingual via navigator.language and respectful of prefers-reduced-motion. The ten index.html files are never touched: nginx injects the widget at serve time, and everything ships on a fresh TLS subdomain without touching the live corporate domain:
# vhost nginx del subdominio (extracto)
server {
server_name infinito.cybersenseai.com;
root /var/www/infinito;
location / {
sub_filter '</body>'
'<script src="/_infinite/widget.js" defer></script></body>';
sub_filter_once on;
}
# TLS via certbot --nginx; A record nuevo → servidor Hetzner
}
ReplícaloReplicate it
sub_filter), nunca en el código fuente de los sitios.Inject the style switcher at deploy time (nginx sub_filter), never into the sites’ source code.Las guías por sitioPer-site guides