Skip to content

Glosario de Términos: Long Animation Frames API

Published:

Referencia rápida de términos, acrónimos y conceptos relacionados con LoAF y web performance.

📋 Índice (click para expandir)

APIs y Métricas

LoAF (Long Animation Frames API)

API de Chrome 116+ que captura frames de animación lentos (>50ms) con attribution detallada de scripts y funciones responsables. A diferencia de Long Tasks API, LoAF proporciona información específica sobre qué función en qué archivo causó el problema.

Ejemplo:

{
  duration: 264,
  scripts: [{
    sourceURL: "checkout.js",
    sourceFunctionName: "validateCoupon",
    duration: 218
  }]
}

INP (Interaction to Next Paint)

Core Web Vital que mide la latencia de interacciones del usuario durante toda la vida de la página. Captura la peor interacción (P98) entre click, tap, y keyboard input.

Umbrales:

Por qué importa: Desde marzo 2024, INP es Core Web Vital oficial y puede afectar el ranking de búsqueda.

Referencias:


LCP (Largest Contentful Paint)

Core Web Vital que mide cuándo el contenido más grande se renderiza en el viewport. Típicamente es una imagen hero, video, o bloque de texto grande.

Umbrales:

Relación con LoAF: Scripts que se ejecutan durante carga inicial pueden bloquear o retrasar LCP.


CLS (Cumulative Layout Shift)

Core Web Vital que mide estabilidad visual. Suma todos los layout shifts inesperados durante la vida de la página.

Umbrales:


FID (First Input Delay) [Deprecado]

Métrica anterior a INP que medía la latencia de la primera interacción solamente. Reemplazado por INP en marzo 2024.

Por qué se reemplazó: FID solo medía input delay de la primera interacción, ignorando el processing time y todas las interacciones subsecuentes.


TBT (Total Blocking Time)

Métrica de Lighthouse que suma el tiempo bloqueante de todas las Long Tasks entre FCP (First Contentful Paint) y TTI (Time to Interactive).

Relación con LoAF: TBT se calcula a partir de Long Tasks, mientras que LoAF proporciona más detalle (attribution).

Umbral: <200ms es considerado bueno en Lighthouse.


Event Timing API

API que captura métricas de latencia de eventos de usuario (click, keydown, etc.).

Qué proporciona:

Uso típico: Calcular INP en producción.

new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 200) {
      console.log("Slow interaction:", entry.name, entry.duration);
    }
  }
}).observe({ type: "event", buffered: true });

Long Tasks API

API (2017) que captura tareas que exceden 50ms en el main thread.

Qué proporciona:

Relación con LoAF: LoAF es la evolución de Long Tasks con attribution detallada.

Cuándo usar: Fallback para navegadores sin soporte de LoAF (Firefox, Safari).


Conceptos de Performance

Frame

Un ciclo completo de renderizado en el navegador:

  1. JavaScript execution
  2. Style calculation
  3. Layout calculation
  4. Paint
  5. Composite

Objetivo: 60fps = 16.67ms por frame (para animaciones suaves)


Long Animation Frame

Frame que excede 50ms de duración, capturado por LoAF API. El umbral de 50ms se eligió porque representa aproximadamente 3 frames a 60fps.

Por qué 50ms: Es el umbral donde los usuarios comienzan a percibir lag en interacciones.


Long Task

Tarea que excede 50ms en el main thread, capturada por Long Tasks API. Similar a Long Animation Frame pero sin attribution detallada.


Blocking Duration

Parte del frame que excede 50ms y bloquea interacciones del usuario. Es diferente de la duración total del frame.

Fórmula:

blockingDuration = Math.max(0, duration - 50);

Ejemplo:


Attribution

Información detallada sobre QUÉ código causó un problema de performance. Incluye:

Antes de LoAF: Attribution manual en DevTools Con LoAF: Attribution automática en producción


Layout Thrashing / Forced Synchronous Layout

Anti-patrón donde alternas lecturas y escrituras del DOM en un bucle, forzando recálculos de layout costosos.

Ejemplo problemático:

for (let i = 0; i < 100; i++) {
  const height = element.offsetHeight; // READ → fuerza layout
  element.style.width = height + "px"; // WRITE → invalida layout
  // Next iteration: layout recalculation needed again
}
// 100 iteraciones = 100 recálculos de layout

Solución:

// Batch reads
const measurements = elements.map(el => el.offsetHeight);

// Batch writes
elements.forEach((el, i) => (el.style.width = measurements[i] + "px"));

Referencias:


Forced Style and Layout Duration

Tiempo que un script gasta en recálculos forzados de style y layout. Expuesto por LoAF como forcedStyleAndLayoutDuration.

Causas comunes:

Interpretación:


Técnicas de Medición

RUM (Real User Monitoring)

Monitoreo de performance con datos de usuarios reales en producción, en contraste con testing sintético en ambiente controlado.

Ventajas:

Desventajas:


Synthetic Monitoring

Monitoreo con herramientas automatizadas (Lighthouse, WebPageTest) en ambiente controlado.

Ventajas:

Desventajas:


Sampling

Capturar solo un porcentaje de eventos para reducir overhead. Crítico para RUM en producción.

Ejemplo:

if (Math.random() < 0.1) {
  // 10% sampling
  captureLoafData(entry);
}

Reglas de oro:

Por qué funciona: Con suficiente volumen, 10% de muestras es estadísticamente significativo.


Percentiles (P50, P75, P95, P99)

Estadísticas que muestran distribución de valores, más útiles que promedios para performance.

Definiciones:

Por qué no usar promedio:

// 90 frames de 75ms, 8 frames de 125ms, 2 frames de 800ms
avg = 91.5ms   // ❌ Esconde los 2 frames problemáticos
P50 = 75ms     // Experiencia típica
P75 = 75ms     // Experiencia de mayoría
P95 = 800ms    // ✅ Revela los outliers

Referencias:


Buffered Observations

Capturar entries que ocurrieron ANTES de que el observer se registrara. Crítico para capturar eventos durante page load.

observer.observe({
  type: "long-animation-frame",
  buffered: true, // ← Incluye entries anteriores
});

Sin buffered: true: Pierdes todos los frames que ocurrieron antes del observer.


Beacon

Envío de datos a servidor de forma no bloqueante usando navigator.sendBeacon().

Ventajas:

Limitaciones:

const data = JSON.stringify({ metrics: {...} });
const blob = new Blob([data], { type: 'application/json' });
navigator.sendBeacon('/api/analytics', blob);

Propiedades de LoAF

entry.duration

Duración total del frame (JavaScript + Rendering + overhead). Incluye todos los scripts y el rendering subsecuente.

const entry = performance.getEntriesByType("long-animation-frame")[0];
console.log(entry.duration); // ej: 264ms

entry.blockingDuration

Tiempo del frame que excede 50ms y bloquea interacciones del usuario.

blockingDuration = Math.max(0, duration - 50);

entry.renderStart

Timestamp cuando comienza el rendering (después de que JavaScript terminó).

Uso: Calcular cuánto tiempo se empleó en JavaScript vs rendering.

const jsTime = entry.renderStart - entry.startTime;
const renderTime = entry.duration - jsTime;

entry.styleAndLayoutStart

Timestamp cuando comienza el cálculo de style y layout.


entry.scripts

Array de scripts que ejecutaron durante el frame. Cada script tiene su propia attribution.

entry.scripts.forEach(script => {
  console.log(script.sourceURL); // Archivo
  console.log(script.sourceFunctionName); // Función
  console.log(script.duration); // Tiempo
});

script.sourceURL

URL del archivo que contiene el script.

Casos comunes:

Casos especiales:


script.sourceFunctionName

Nombre de la función que se invocó.

Limitaciones:

Implicación para producción: En scripts third-party minificados, este campo es casi inútil. Identifica por sourceURL (dominio) en su lugar.


script.duration

Tiempo que ese script específico consumió ejecutando JavaScript. No incluye rendering.

Diferencia con frame duration:

Frame duration: 200ms      // Frame completo
Script duration: 120ms     // Solo JavaScript de ese script
Difference: 80ms           // Rendering + otros scripts + overhead

script.forcedStyleAndLayoutDuration

Tiempo que ese script gastó en recálculos forzados de style y layout (layout thrashing).

Interpretación:

if (script.forcedStyleAndLayoutDuration > script.duration * 0.3) {
  console.warn("Layout thrashing detected!");
}

script.invoker

Qué invocó el script. Puede ser un event handler, un timer, etc.

Ejemplos:

Uso: Correlacionar script con interacción o evento específico.


Términos de Optimización

Code Splitting

Dividir código en chunks más pequeños que cargan on-demand, reduciendo el bundle inicial.

Ejemplo (Webpack):

import(/* webpackChunkName: "heavy" */ "./heavy-module.js").then(module =>
  module.doSomething()
);

Ejemplo (Vite):

// Vite soporta dynamic imports nativamente
import("./heavy-module.js").then(module => module.doSomething());

// Con React + Vite
const HeavyComponent = lazy(() => import("./HeavyComponent"));

// Con Vue + Vite
const HeavyComponent = defineAsyncComponent(
  () => import("./HeavyComponent.vue")
);

Lazy Loading

Cargar recursos solo cuando se necesitan, no durante la carga inicial.

Ejemplo (third-party script):

// ❌ Carga inmediata
<script src="analytics.js"></script>;

// ✅ Lazy load después de interacción
button.addEventListener(
  "click",
  () => {
    import("./analytics.js").then(module => module.track(event));
  },
  { once: true }
);

Debouncing

Retrasar ejecución de función hasta que pasen X milisegundos sin nuevos eventos.

Uso típico: Input field que dispara búsqueda.

const debounce = (fn, delay) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), delay);
  };
};

input.addEventListener("input", debounce(search, 300));

Throttling

Limitar frecuencia de ejecución de función (máximo 1 vez cada X ms).

Uso típico: Scroll handler.

const throttle = (fn, limit) => {
  let inThrottle;
  return (...args) => {
    if (!inThrottle) {
      fn(...args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
};

window.addEventListener("scroll", throttle(onScroll, 100));

Memoization

Cachear resultados de funciones costosas para evitar recalcular.

const memoize = fn => {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};

const expensiveCalc = memoize(n => {
  // Cálculo costoso
  return result;
});

FastDOM

Librería que batches lecturas y escrituras del DOM para evitar layout thrashing.

import fastdom from "fastdom";

// Batch reads
fastdom.measure(() => {
  const height = element.offsetHeight;

  // Batch writes
  fastdom.mutate(() => {
    element.style.width = height + "px";
  });
});

Referencias:


requestIdleCallback

API para ejecutar código durante periodos idle del navegador, sin afectar performance.

requestIdleCallback(deadline => {
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    const task = tasks.pop();
    task();
  }
});

Cuidado: No soportado en Safari (usar polyfill).


requestAnimationFrame

API para sincronizar código con refresh rate del display (típicamente 60fps = ~16.67ms).

Uso típico: Animaciones suaves.

function animate() {
  // Update animation
  element.style.transform = `translateX(${x}px)`;

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

Scripts y Orígenes

First-party Script

Script del mismo dominio que la página.

Ejemplo:


Third-party Script

Script de dominio diferente a la página.

Ejemplos comunes:

Estadística: Los third-party scripts tienen un impacto significativo en performance, bloqueando el main thread en hasta 90% de páginas móviles. Fuente: HTTP Archive Web Almanac 2022


Inline Script

Código JavaScript dentro de <script> sin atributo src.

<script>
  console.log("Inline script");
</script>

En LoAF: sourceURL estará vacío o será el URL de la página.


External Script

Código JavaScript cargado desde archivo externo vía <script src="">.

<script src="/app.js"></script>

Minified Code

Código comprimido eliminando espacios, comentarios, y renombrando variables.

Ejemplo:

// Original
function calculateTotalPrice(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// Minificado
function a(b) {
  return b.reduce((c, d) => c + d.price, 0);
}

Impacto en LoAF: sourceFunctionName será críptico (a, b, Hc).


Source Maps

Archivos que mapean código minificado a código original para debugging.

Formato: app.min.js.map

En DevTools: Permite ver código original incluso con bundle minificado.

Limitación con LoAF: La API nativa de LoAF NO procesa Source Maps automáticamente. En producción devuelve nombres minificados (sourceFunctionName: "a"). Solo DevTools correlaciona Source Maps para mostrar nombres originales cuando está disponible.

Workaround para RUM: Herramientas como Sentry pueden procesar Source Maps del lado servidor para mapear stack traces de LoAF a código original.


Herramientas

DevTools Performance Panel

Panel de Chrome DevTools para grabar y analizar performance con flamegraphs, timeline, y métricas.

Acceso:


Lighthouse

Herramienta de auditoría automatizada de Google. Mide performance, accessibility, SEO, best practices.

Acceso:

Métricas:


WebPageTest

Herramienta online para testing de performance con dispositivos reales y múltiples ubicaciones.

URL: webpagetest.org

Ventajas:


Chrome User Experience Report (CrUX)

Dataset público con métricas de usuarios reales de Chrome (field data).

Acceso:


Web Vitals Extension

Extension de Chrome que muestra Core Web Vitals en tiempo real en un HUD overlay.

Instalación: Chrome Web Store


Referencias Rápidas

Umbrales Críticos

Core Web Vitals (Umbrales Oficiales de Google)

MétricaGoodNeeds ImprovementPoorFuente
INP≤200ms200-500ms>500msweb.dev/inp
LCP≤2.5s2.5-4s>4sweb.dev/lcp
CLS≤0.10.1-0.25>0.25web.dev/cls

Lighthouse Metrics (Métricas Sintéticas)

MétricaGoodNeeds ImprovementPoor
TBT≤200ms200-600ms>600ms

LoAF Frames (Recomendaciones Prácticas - No Oficiales)

⚠️ Nota: Los siguientes umbrales son recomendaciones prácticas para clasificar frames de LoAF, no son métricas oficiales de Google. Usa estos valores como guía para priorizar optimizaciones.

ConceptoGoodMediumHighCritical
Frame duration≤50ms50-100ms100-200ms>200ms
Blocking duration0ms<50ms50-100ms>100ms

Justificación de umbrales de frame:


Compatibilidad

APIChromeEdgeFirefoxSafari
LoAF116+ ✅116+ ✅
Long Tasks58+ ✅79+ ✅
Event Timing76+ ✅79+ ✅89+ ✅
PerformanceObserver52+ ✅79+ ✅57+ ✅11+ ✅

Cobertura LoAF: ~82% usuarios globales (Chrome + Edge)


APIs Relacionadas (Cronología)

AñoAPIPropósito
2012Navigation TimingMétricas de navegación
2013Resource TimingTiming de recursos
2015User TimingCustom marks y measures
2017Long Tasks APIDetectar tareas >50ms
2019Event TimingLatencia de eventos
2019Layout InstabilityMedir CLS
2019Largest Contentful PaintMedir LCP
2023Long Animation Frames APIAttribution detallada ⭐

Recursos de Aprendizaje

Documentación Oficial

Artículos y Guías

Herramientas y Recursos

Casos de Éxito

Taboola: Optimización de INP con técnicas avanzadas

Recursos adicionales de casos reales:



Previous Post
Fundamentos de Long Animation Frames API
Next Post
Charlas 2023