Remotion: vídeo programático con React

Un vídeo puede ser un componente: recibe datos, calcula cada fotograma y produce una pieza reproducible. Remotion aplica ese modelo a React sin convertir la creatividad en una caja negra.

Por David Moya · · 16 min lectura

En este artículo

  1. Qué cambia con Remotion
  2. Proyecto y Composition
  3. Sequence, Audio e Img
  4. Animación determinista
  5. Vídeo dirigido por JSON
  6. Audio y música
  7. Render responsable
  8. Preguntas frecuentes

Qué cambia con Remotion

Remotion permite describir un vídeo con componentes React. No sustituye todas las tareas de un editor tradicional: su ventaja aparece cuando una pieza tiene reglas, datos variables o muchas versiones. Una cabecera, una gráfica, un subtítulo y una llamada a la acción pueden expresarse como componentes que reciben propiedades. El resultado deja de depender de mover elementos a mano y pasa a depender de una entrada comprobable.

El modelo mental es sencillo. Una Composition define las dimensiones, la velocidad de fotogramas y la duración. Durante el render, el componente recibe un número de fotograma. A partir de ese número calcula posición, opacidad, escala, texto visible y cualquier otro estado visual. A treinta fotogramas por segundo, el segundo dos empieza en el fotograma sesenta. La aritmética temporal es explícita y puede revisarse como cualquier otra lógica.

Este enfoque encaja bien con informes periódicos, vídeos personalizados, catálogos, visualizaciones de datos e intros coherentes. Encaja peor cuando la pieza depende sobre todo de montaje exploratorio, interpretación humana de material grabado o retoque manual único. Programar un vídeo solo compensa si las reglas aportan repetibilidad, no porque el código sea un fin en sí mismo.

Preparar el proyecto y registrar una Composition

El generador oficial crea la estructura inicial y abre el entorno de previsualización. Conviene empezar con una sola composición pequeña, comprobar el resultado y extraer componentes únicamente cuando exista repetición real.

npx create-video@latest mi-video
cd mi-video
npm start

La Composition es el contrato del vídeo. El identificador se usa al renderizar; el componente contiene la escena; y duración, velocidad y tamaño determinan el lienzo. Este ejemplo produce una pieza horizontal de seis segundos. Las propiedades por defecto permiten verla sin preparar todavía una fuente externa.

import {Composition} from 'remotion';
import {Promo, PromoProps} from './Promo';

export const RemotionRoot = () => (
  <Composition<PromoProps>
    id="Promo"
    component={Promo}
    durationInFrames={180}
    fps={30}
    width={1920}
    height={1080}
    defaultProps={{title: 'Informe semanal', metric: '42 tareas'}}
  />
);

No disperses constantes temporales sin nombre por todo el árbol. Define el ritmo de la pieza cerca de la composición o deriva segundos a partir de fps. Si cambias la velocidad de fotogramas, los tiempos expresados como 2 * fps conservan su significado; un literal aislado como 60 no lo explica.

Construir escenas con Sequence, Audio e Img

AbsoluteFill ocupa el fotograma completo. Sequence decide cuándo se monta un subárbol y presenta a sus hijos un fotograma relativo a su inicio. Esta última propiedad evita que un título reutilizable tenga que conocer su posición absoluta dentro del vídeo.

import {AbsoluteFill, Img, Sequence, staticFile} from 'remotion';

export const Promo = () => (
  <AbsoluteFill style={{backgroundColor: '#0f0f18'}}>
    <Img src={staticFile('fondo.png')} style={{width: '100%'}} />
    <Sequence from={0} durationInFrames={75}>
      <Titulo texto="Un dato, una historia" />
    </Sequence>
    <Sequence from={75} durationInFrames={105}>
      <Resumen />
    </Sequence>
  </AbsoluteFill>
);

Img participa en el ciclo de carga de Remotion y es preferible a una etiqueta de imagen corriente para recursos que deben estar listos al capturar el fotograma. staticFile() referencia archivos públicos sin incrustar rutas frágiles. Para recursos externos, verifica disponibilidad, permisos y estabilidad antes de depender de ellos en un lote.

BloqueResponsabilidadError habitual
CompositionContrato global del vídeoMezclar formatos incompatibles en un solo lienzo
SequenceVentana temporal de una escenaSolapar contenido sin intención
ImgImagen sincronizada con el renderUsar un recurso remoto inestable
AudioPista sonora y volumenSaturar al sumar voz, música y efectos

spring(), interpolate() y un typewriter determinista

Una animación de Remotion no debe depender de temporizadores, del reloj del sistema ni de mutaciones acumuladas. Cada fotograma puede calcularse o reintentarse de forma independiente. Si el fotograma y las propiedades son iguales, la imagen debe ser igual. spring() crea una progresión física a partir del fotograma; interpolate() transforma un rango temporal en un rango visual.

import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion';

const Badge = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const scale = spring({frame, fps, config: {damping: 14}});
  const opacity = interpolate(frame, [0, 12], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });
  return <div style={{opacity, transform: `scale(${scale})`}}>Nuevo</div>;
};

El recorte o clamp es importante: sin él, la interpolación sigue más allá del intervalo. Eso puede producir opacidades negativas o desplazamientos que crecen durante toda la escena. Para una salida animada, define otro intervalo y combina ambos valores conscientemente.

Un efecto máquina de escribir tampoco necesita estado. Basta con calcular cuántos caracteres corresponden al fotograma actual. Así, saltar directamente al fotograma cuarenta muestra exactamente el texto esperado.

const Typewriter = ({text}: {text: string}) => {
  const frame = useCurrentFrame();
  const count = Math.min(text.length, Math.floor(frame / 2));
  const cursor = count < text.length && Math.floor(frame / 10) % 2 === 0;
  return <span>{text.slice(0, count)}{cursor ? '|' : ''}</span>;
};

Evita setInterval, animaciones CSS basadas en tiempo real y números aleatorios sin semilla. En la previsualización pueden parecer correctos, pero el render no tiene obligación de visitar los fotogramas en orden. La función visual debe ser pura respecto al fotograma y a sus entradas.

Del JSON a un vídeo parametrizado

El vídeo dirigido por datos separa plantilla y contenido. El componente decide jerarquía, movimiento y estilo; el JSON aporta el título, los valores y la lista de escenas. Antes de renderizar, valida tipos, campos obligatorios, longitudes y rutas de recursos. Una entrada defectuosa no debería generar silenciosamente una pieza engañosa.

{
  "title": "Resumen del proyecto",
  "metric": "12 entregas",
  "items": ["Diseño", "Desarrollo", "Revisión"]
}
export type PromoProps = {
  title: string;
  metric: string;
  items: string[];
};

export const Promo = ({title, metric, items}: PromoProps) => (
  <AbsoluteFill style={{padding: 96, background: '#0f0f18', color: 'white'}}>
    <Sequence from={0} durationInFrames={60}><Typewriter text={title} /></Sequence>
    <Sequence from={60} durationInFrames={60}><Dato value={metric} /></Sequence>
    <Sequence from={120}>
      <ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>
    </Sequence>
  </AbsoluteFill>
);

Para un lote, cada objeto puede convertirse en las propiedades de una ejecución. Usa identificadores estables para los archivos de salida y guarda la entrada junto al resultado o en un registro auditable. Si el contenido procede de una IA, añade revisión humana cuando pueda afectar a personas, marcas, cifras o afirmaciones públicas. Automatizar la composición no convierte los datos en verdaderos.

También conviene diseñar límites: qué ocurre si un título es demasiado largo, falta una imagen o una lista contiene veinte elementos. Recortar texto sin avisar oculta el problema. Es mejor rechazar la entrada o elegir explícitamente una variante de plantilla preparada para ese tamaño.

Audio, música y sincronización

La voz suele marcar el ritmo. Obtén los tiempos de cada bloque del guion o de una transcripción temporizada y conviértelos a fotogramas con Math.round(segundos * fps). Después coloca texto, imágenes y efectos dentro de Sequence. El redondeo explícito evita valores fraccionarios y mantiene una frontera clara.

import {Audio, Sequence, staticFile} from 'remotion';

<Audio src={staticFile('audio/musica.mp3')} volume={0.12} />
<Sequence from={60}>
  <Audio src={staticFile('audio/voz.mp3')} volume={1} />
</Sequence>
<Sequence from={90} durationInFrames={30}>
  <Audio src={staticFile('audio/transicion.mp3')} volume={0.35} />
</Sequence>

Escucha la mezcla completa, no cada pista aislada. La música debe dejar espacio a la voz y los efectos no deben provocar picos molestos. Usa solo grabaciones, músicas, voces e imágenes para las que tengas autorización compatible con la publicación. Conserva la atribución o la licencia cuando corresponda y no clones voces de terceros sin consentimiento.

La duración del vídeo debe responder al contenido real. Si la voz termina antes que la composición, decide si habrá cierre visual, silencio o fundido; no dejes fotogramas vacíos por accidente. Si la voz es más larga, ampliar la Composition es preferible a cortar una frase.

Render responsable: probar, limitar y observar

El render local permite revisar una composición concreta con propiedades controladas. Primero genera una muestra corta y observa fotogramas del inicio, las transiciones y el final. Solo después lanza el vídeo completo o el lote.

npx remotion render src/index.ts Promo out/promo.mp4 \
  --props='{"title":"Resumen","metric":"12 entregas","items":["Diseño","Revisión"]}'

Renderizar consume CPU, memoria, almacenamiento y energía. Más concurrencia no siempre significa terminar antes: puede agotar memoria, competir por disco o hacer inestable el proceso. Mide en la máquina real, fija un límite razonable y evita generar de nuevo una salida cuya entrada no ha cambiado. Un hash de plantilla, propiedades y recursos permite detectar duplicados sin inventar una infraestructura compleja.

La nube puede ser adecuada cuando el volumen y el tiempo de entrega lo justifican, pero no es un requisito para aprender ni para producir piezas pequeñas. Decide con mediciones propias y consulta la documentación vigente del entorno de render elegido. Evitar cifras universales de tiempo o coste es más honesto: dependen del códec, la resolución, los recursos, la composición y la infraestructura.

Preguntas frecuentes

¿Qué es Remotion y para qué sirve?

Es un entorno para definir vídeos mediante componentes React. Resulta especialmente útil cuando el contenido debe ser reproducible, parametrizable o generado a partir de datos. El mismo sistema de diseño puede producir piezas distintas sin reconstruir manualmente cada línea de tiempo.

¿Necesito saber edición de vídeo para usar Remotion?

No necesitas dominar una línea de tiempo visual, pero sí comprender React, CSS y la relación entre fotogramas, duración y velocidad. El criterio audiovisual sigue siendo necesario: ritmo, legibilidad, mezcla sonora y composición no aparecen por escribir código.

¿Por qué las animaciones deben ser deterministas?

Porque cada fotograma puede renderizarse de forma aislada o en otro orden. El mismo fotograma y las mismas propiedades deben producir la misma imagen. Por eso se calcula desde useCurrentFrame() en lugar de depender del reloj o de estado acumulado.

¿Cómo se genera un vídeo desde JSON?

Se tipan y validan los datos, se pasan como propiedades de la Composition y el componente convierte esos valores en escenas, textos e imágenes. Cada objeto validado puede alimentar un render independiente con una salida identificable.

¿Cómo se sincronizan voz, música y escenas?

Se usa una velocidad de fotogramas común y se convierten los tiempos del guion o la locución a fotogramas. Sequence sitúa cada bloque visual y Audio reproduce cada pista en su intervalo. La mezcla final debe escucharse completa.

¿Qué significa renderizar de forma responsable?

Significa validar datos y derechos, probar una muestra, limitar la concurrencia, registrar resultados y evitar trabajos duplicados. También implica revisar el contenido generado antes de publicarlo y no usar voces o recursos ajenos sin autorización.

📚 Aprende más en el curso

Este artículo complementa el Módulo M20: Remotion: vídeo programático. Incluye vídeo, quiz, flashcards con repaso espaciado y proyecto práctico.

Ir al módulo M20 Repasar con FSRS