Código de la charla «Render as a Service: de screenshots dinámicos a vídeos generados con arquitectura serverless», presentada en CommitConf 2026.
Todas las variantes expuestas en la charla comparten el mismo contrato público: una URL identifica la plantilla, los datos y el formato de salida.
Una página de React recibe un identificador, resuelve los datos y Puppeteer captura el resultado. Es la opción más sencilla para tarjetas sociales, acreditaciones, certificados o fichas de producto.
/static-event/40.png
/static-sponsor/100.jpg
La plantilla utiliza GSAP. Chromium avanza de forma determinista, genera cada frame y FFmpeg los codifica.
/static-animated-event/40.mp4?length=5
/static-animated-sponsor/100.gif?length=5
Remotion expresa la animación como una función del número de frame. La Lambda selecciona una composición y genera el vídeo con @remotion/renderer.
/video-event-t3f/40.mp4
/video-event-commitconf/58.mp4
Parte del código de animación y vídeo se creó y revisó utilizando las siguientes skills:
| Tecnología | Skills utilizadas | Aplicación en el proyecto |
|---|---|---|
| Remotion | remotion-best-practices |
Composiciones, secuencias, interpolaciones, audio y renderizado determinista. |
| GSAP | gsap-core, gsap-timeline y gsap-react |
Tweens, timelines, integración con React y limpieza mediante gsap.context(). |
Puedes instalarlas en un agente compatible mediante:
npx skills add remotion-dev/skills
npx skills add https://github.com/greensock/gsap-skillsSu instalación es opcional y solo resulta útil para desarrollar, modificar o ampliar las plantillas.
sequenceDiagram
participant CLIENT as Cliente
participant CF as CloudFront
participant STORE as Amazon S3
participant GEN as Lambda generadora
participant TEMPLATE as Plantilla React
participant RENDER as Motor de render
CLIENT->>CF: Solicita una imagen o un vídeo
CF->>STORE: Busca el recurso generado
alt El recurso existe
STORE-->>CF: Devuelve el archivo
CF-->>CLIENT: Sirve el recurso almacenado
else El recurso no existe
STORE-->>CF: Responde con 403 o 404
CF->>GEN: Reenvía la petición
GEN->>TEMPLATE: Carga la plantilla y sus datos
TEMPLATE-->>GEN: Devuelve HTML o una composición
GEN->>RENDER: Solicita el render
Note right of RENDER: Puppeteer, GSAP con FFmpeg o Remotion
RENDER-->>GEN: Devuelve la imagen o el vídeo
GEN->>STORE: Guarda el recurso generado
STORE-->>GEN: Confirma el almacenamiento
GEN-->>CF: Devuelve el resultado
CF-->>CLIENT: Sirve el nuevo recurso
end
CloudFront utiliza un grupo de orígenes:
- Busca el recurso en el bucket de resultados.
- Si S3 responde con
403o404, invoca la Lambda de generación. - La Lambda selecciona el renderer según la primera parte de la URL.
- El resultado se devuelve al cliente y se almacena en S3.
- Una segunda Lambda permite borrar el objeto e invalidar CloudFront.
apps/
├── templates/ Páginas estáticas, animaciones GSAP y composiciones Remotion
└── renderer/ Lambdas, renderers, almacenamiento e infraestructura CDK
apps/templates se exporta como un sitio estático. apps/renderer captura ese sitio mediante Chromium o utiliza su bundle de Remotion.
Los datos incluidos están reducidos a dos charlas de T3chFest, dos sesiones de CommitConf y dos sponsors. Solo conservan los campos que consumen las demos.
- Node.js 22 o superior.
- Google Chrome o Chromium para renderizar localmente.
- Binario de FFmpeg disponible en
PATHpara generar MP4 y GIF desde HTML animado (consultaapps/renderer/scripts/download-ffmpeg.sh). - AWS CLI y AWS CDK para desplegar.
- Credenciales de AWS configuradas si se utiliza la infraestructura.
cp .env.example .env
npm installArranca las plantillas en una terminal:
npm run dev:templatesEste comando inicia Next.js en el puerto 3000 y genera en disco el bundle que utiliza el renderer local de Remotion. Si modificas una composición de Remotion, reinicia el comando para regenerar el bundle.
Las páginas de demostración estarán disponibles en:
http://localhost:3000/static/event/?id=40
http://localhost:3000/static/sponsor/?id=100
http://localhost:3000/static-animated/event/?id=40&now
http://localhost:3000/video/event-t3f/?id=40
http://localhost:3000/video/event-commitconf/?id=58
En otra terminal, genera recursos con el mismo handler usado por Lambda:
npm run render:local -- main /static-event/40.png
npm run render:local -- main '/static-animated-event/40.mp4?length=5'
npm run render:local -- main /video-event-t3f/40.mp4
npm run render:local -- main /video-event-commitconf/58.mp4Los resultados se guardan en apps/renderer/output/.
Para trabajar directamente con las composiciones Remotion:
npm run remotionTambién puedes renderizar una composición con la CLI de Remotion:
npm run render -- EventT3f output/t3f.mp4 --props='{"id":40}'
npm run render -- EventCommitconf output/commitconf.mp4 --props='{"id":58}'Copia .env.example como .env y adapta estas variables:
| Variable | Uso |
|---|---|
AWS_ACCOUNT_ID |
Cuenta donde CDK crea los recursos. |
AWS_REGION |
Región de las Lambdas y buckets. |
AWS_PROFILE |
Perfil utilizado por AWS CLI al subir las plantillas. |
BUCKET_NAME |
Bucket privado de recursos generados. |
TEMPLATE_BUCKET_NAME |
Bucket público con las plantillas exportadas. |
CF_SECRET |
Secreto compartido entre CloudFront y la Lambda generadora. |
INVALIDATE_SECRET |
Token de la Lambda de invalidación. |
TEMPLATE_BUCKET_URL |
Host de plantillas usado durante el desarrollo local. |
DOMAIN_NAME |
Dominio público opcional. |
CERTIFICATE_ARN |
Certificado ACM opcional para el dominio. |
BASE_DOMAIN_NAME |
Zona de Route53 opcional. |
npm run buildLa compilación genera el bundle de las Lambdas y la exportación estática de Next.js. Los artefactos generados están excluidos de Git.
Descarga el binario de FFmpeg que se empaquetará como una Lambda Layer:
cd apps/renderer
./scripts/download-ffmpeg.sh
cd ../..
npm run cdk:synthDespliega primero la infraestructura y después las plantillas:
npm run deploy:infra
npm run deploy:templatesEl despliegue de CDK crea:
- Un bucket privado para resultados.
- Un bucket web para plantillas.
- Una Lambda generadora.
- Una Lambda de invalidación.
- Una distribución CloudFront con failover de S3 a Lambda.
- Registros Route53 cuando se configura un dominio.
Cuando cambian los datos de un recurso ya generado, elimina el objeto de S3 e invalida CloudFront:
curl -X POST "$INVALIDATE_URL" \
-H "x-token: $INVALIDATE_SECRET" \
-H "Content-Type: application/json" \
-d '{"path":"/static-event/40.png"}'- Crea la página o composición en
apps/templates. - Añade su tipo a
VALID_TYPESenapps/renderer/src/lib/types.ts. - Registra la ruta, dimensiones, renderer y formatos en
apps/renderer/src/lib/templates.ts. - Compila y prueba el nuevo endpoint localmente.
- Vuelve a desplegar las plantillas y la infraestructura.
- Es una demostración técnica, no una plataforma multitenant terminada.
- CloudFront limita a 180 segundos la espera del origen. Los vídeos largos deberían usar un flujo asíncrono.
- El coste y el tiempo aumentan con la resolución, el número de frames y la duración.
- Los renders de HTML animado están limitados a 15 segundos en esta demo.
- Las plantillas dependen de imágenes remotas; un recurso externo no disponible puede hacer fallar el render.
- La autenticación con secretos en cabeceras es suficiente para la demo, pero una API pública debería añadir autorización, rate limiting y observabilidad.
