FPS (Frames Per Second) es una métrica que muestra cuántos fotogramas individuales renderiza un sistema gráfico en un segundo. En el desarrollo móvil, FPS es un indicador estándar de rendimiento de la interfaz de usuario: cuanto más alto es el FPS, más suaves son las animaciones y más receptiva es la interfaz. Según Google Android Performance, 2025, el valor objetivo de FPS para aplicaciones móviles es de 60 fotogramas por segundo, el umbral en el que el ojo humano percibe el movimiento como continuo y fluido.
Puntos clave
FPS (Frames Per Second) es una unidad de medida de la frecuencia de fotogramas utilizada en gráficos por computadora, video e interfaces móviles. Cada fotograma es una imagen estática que se muestra en la pantalla durante un breve período de tiempo. Con cambios rápidos de fotogramas, el cerebro los percibe como movimiento continuo; este efecto se llama persistencia de la visión. Para las aplicaciones móviles, FPS es una métrica crítica porque cualquier fotograma perdido convierte una animación fluida en un tirón notable. La aplicación debe renderizar cada fotograma estrictamente dentro del presupuesto de tiempo: 16.6 ms para 60 FPS, 11.1 ms para 90 FPS, 8.3 ms para 120 FPS.
FPS se mide no solo para la interfaz de usuario, sino también para juegos, video y cámara. En los juegos, FPS depende de la complejidad de la escena, la calidad de las texturas y la potencia de la GPU. En el video, FPS es fijo (24, 30, 60 fps) y está determinado por el contenido. En las aplicaciones móviles, FPS depende de la eficiencia del código de la interfaz de usuario: complejidad del diseño, cantidad de vistas, frecuencia de redibujado y trabajo del GC (Garbage Collection). Según Apple WWDC 2022, el FPS promedio en una aplicación puede caer entre un 10 y un 15 % debido a actualizaciones ineficientes de colecciones (reloadData en lugar de insert/delete/dequeueReusableCell). Medir FPS en tiempo real es una práctica estándar para los ingenieros de control de calidad y los desarrolladores que trabajan en rendimiento.
El cálculo del FPS en una aplicación móvil se basa en medir el tiempo entre fotogramas consecutivos. La fórmula más simple: FPS = 1000 / deltaTimeMs, donde deltaTimeMs es el intervalo entre la finalización del fotograma anterior y la finalización del actual. Si el fotograma actual se renderizó en 20 ms, FPS = 1000 / 20 = 50. Sin embargo, en la práctica, el FPS rara vez es estable incluso dentro de un solo segundo: un perfil típico incluye fotogramas de 12 a 16 ms intercalados con fotogramas omitidos (jank) o lentos (40 a 60 ms). Por lo tanto, el FPS se mide como un promedio móvil de 1 a 5 segundos o como percentiles de la distribución del tiempo de fotograma.
En Android, el FPS se calcula mediante Choreographer, que recibe una devolución de llamada de VSync (pulso de sincronización de la pantalla). Cada devolución de llamada corresponde a un fotograma. Si la devolución de llamada no llega, el fotograma se omite. Choreographer permite medir la cantidad exacta de fotogramas por segundo y la cantidad de fotogramas omitidos. En iOS, CADisplayLink funciona de manera similar: se llama cada vez que la pantalla está lista para renderizar un nuevo fotograma. La propiedad timestamp contiene la hora exacta del último fotograma, y targetTimestamp la hora esperada del siguiente. La diferencia entre ellos es el presupuesto de tiempo para el fotograma actual.
El código en Swift demuestra un monitoreo simple de FPS mediante CADisplayLink. El contador frameCount se incrementa con cada llamada, y una vez por segundo se calcula el FPS real.
class FpsCounter {
private var displayLink: CADisplayLink?
private var frameCount = 0
private var lastTime = TimeInterval(0)
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(countFrame)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func countFrame() {
frameCount += 1
let now = Date().timeIntervalSince1970
if now - lastTime >= 1.0 {
print("FPS: \(frameCount)")
frameCount = 0
lastTime = now
}
}
}
El estándar de 60 FPS (o 60 Hz) se consolidó en la industria por varias razones. La primera es fisiológica: el ojo humano no distingue fotogramas individuales en frecuencias superiores a 50–60 Hz, percibiéndolos como movimiento fluido. Este umbral se denomina Critical Flicker Fusion (CFF). La segunda es histórica: los primeros tubos de rayos catódicos (CRT) funcionaban a 60 Hz en EE. UU. (NTSC) y 50 Hz en Europa (PAL). Las pantallas LCD modernas heredaron esta frecuencia. La tercera es técnica: para las animaciones de la interfaz de usuario, 60 FPS proporciona una latencia de respuesta al tacto de menos de un milisegundo, lo cual es fundamental para la entrada de texto, el desplazamiento y la acción de arrastrar.
Para los desarrolladores móviles, 60 FPS no es solo una recomendación, sino un presupuesto estricto de 16.6 ms por fotograma. Este presupuesto se divide entre todas las fases de renderizado: Entrada (1–2 ms), Animación (2–3 ms), Diseño (3–5 ms), Dibujo (3–5 ms) e Intercambio (1–2 ms). Si alguna fase supera su subpresupuesto, es posible que el fotograma no entre en los 16.6 ms. Google Android Performance recomienda mantenerse dentro de los 12–14 ms para la preparación del fotograma, dejando 2–4 ms de margen para interrupciones del sistema (GC, subprocesos en segundo plano). Según Firebase Performance, las aplicaciones con un FPS promedio inferior a 52 y un P99 FPS inferior a 30 reciben un 35 % más de quejas sobre el rendimiento en las reseñas de Google Play.
FPS y tiempo de fotograma son dos caras de la misma métrica, y es importante no confundirlos. FPS es velocidad, el tiempo de fotograma es latencia. A 60 FPS, cada fotograma toma 16.6 ms. A 30 FPS, 33.3 ms. Pero FPS es una métrica no lineal: una caída de 60 a 30 FPS significa que el tiempo de fotograma se duplicó, mientras que una caída de 30 a 20 significa un aumento de 1.5 veces. Por lo tanto, los generadores de perfiles muestran el tiempo de fotograma en lugar de FPS, lo que permite ver los fotogramas problemáticos en lugar de una frecuencia promediada. Por ejemplo, un promedio de 55 FPS puede ocultar el hecho de que el 5 % de los fotogramas tienen un tiempo de fotograma de 50 a 100 ms; estos fotogramas causan Jank pero no afectan significativamente el FPS promedio.
Al analizar el rendimiento, se recomienda observar no el FPS promedio, sino el histograma del tiempo de fotograma. En Android Studio Profiler e iOS Instruments, el tiempo de fotograma se muestra como una escala donde la zona verde es hasta 16.6 ms (60 FPS), la amarilla es de 16.6 a 33.3 ms (30–60 FPS) y la roja es más de 33.3 ms (menos de 30 FPS). Cada columna roja es un retraso notable para el usuario. Una regla práctica: P95 Frame Time (el 95 % de los fotogramas entran en X ms) es una métrica más confiable que el FPS promedio. Si el P95 Frame Time supera los 32 ms (30 FPS), la aplicación se percibe como lenta incluso con un FPS promedio de 50.
Una función en Kotlin para convertir una matriz de tiempos de fotograma a FPS con percentiles. Devuelve no solo el FPS promedio, sino también P50, P90 y P99 para un análisis detallado.
data class FpsReport(
val average: Float,
val p50: Float,
val p90: Float,
val p99: Float
)
fun List<Long>.toFpsReport(): FpsReport {
val fpsValues = this.map { ms ->
if (ms > 0) 1000f / ms else 0f
}.sorted()
return FpsReport(
average = fpsValues.average().toFloat(),
p50 = fpsValues[fpsValues.size / 2],
p90 = fpsValues[(fpsValues.size * 90 / 100)],
p99 = fpsValues[(fpsValues.size * 99 / 100)]
)
}
Los dispositivos móviles modernos con pantallas de 90, 120 y 144 Hz imponen nuevos requisitos al FPS. Si una aplicación ofrece 60 FPS en una pantalla de 120 Hz, el usuario ve microtirones porque cada segundo ciclo de actualización de la pantalla recibe el mismo fotograma. Para mantener 120 FPS, el presupuesto por fotograma se reduce de 16.6 a 8.3 ms, lo que requiere un código de renderizado dos veces más eficiente. Según los desarrolladores de Android (Google I/O 2023), para lograr 120 FPS estables es necesario: evitar asignaciones en el ciclo de dibujo, minimizar la cantidad de vistas en la jerarquía (menos de 80), abandonar los drawables pesados en favor de VectorDrawable y usar surfaceView para gráficos complejos.
La situación es similar en iOS: el iPhone Pro con ProMotion (120 Hz) requiere el doble de fotogramas, pero el tiempo por fotograma se reduce a la mitad. Apple señala que no todas las animaciones deben funcionar a 120 FPS; Core Animation reduce automáticamente la frecuencia de fotogramas para elementos estáticos o de cambio lento. Sin embargo, el desplazamiento, las animaciones de gestos y las transiciones deben ofrecer 120 FPS para una sensación “sedosa”. Los principales problemas al pasar de 60 a 120 FPS son: mayor consumo de energía (25–40 % para la GPU), calentamiento del dispositivo y limitación térmica (throttling), cuando la frecuencia de fotogramas cae debido al sobrecalentamiento. Se recomienda implementar un mecanismo de respaldo: si el tiempo de fotograma supera constantemente los 8.3 ms, reducir programáticamente la frecuencia de fotogramas objetivo a 60 FPS en lugar de esperar la limitación del sistema.
El código en Java para Android determina si el dispositivo puede admitir 120 FPS y cambia el modo de renderizado. Se utiliza Display.getMode para determinar las frecuencias de actualización admitidas.
class FpsModeSwitcher {
static boolean canDo120Fps(Activity activity) {
Display display = activity.getWindowManager()
.getDefaultDisplay();
for (Display.Mode mode : display.getSupportedModes()) {
if (mode.getRefreshRate() >= 120f) {
return true;
}
}
return false;
}
}
La optimización del FPS requiere un enfoque sistemático, que comienza con la creación de perfiles y termina con la refactorización de las áreas problemáticas. El primer paso es medir el FPS actual con un generador de perfiles. El segundo paso es encontrar los fotogramas que superan el presupuesto. En Android, esto se puede hacer mediante GPU Profiling o Perfetto. En iOS, mediante Instruments con la plantilla Core Animation. El tercer paso es eliminar las causas: reducir el overdraw, disminuir la profundidad de la jerarquía de vistas, reemplazar la fase de diseño con ConstraintLayout, agregar ViewHolder Recycling y mover los cálculos pesados a un subproceso en segundo plano.
Las optimizaciones específicas de FPS incluyen: Frame Pacing, un mecanismo que distribuye uniformemente el tiempo entre los fotogramas para evitar “ráfagas” de fotogramas rápidos y lentos. En Android, Choreographer.FrameCallback con un intervalo fijo permite implementar Frame Pacing. En iOS, CADisplayLink.preferredFrameRateRange hace lo mismo. El segundo método es Triple Buffering: el sistema usa tres búferes en lugar de dos, lo que permite que la GPU comience a renderizar el siguiente fotograma sin esperar a que se libere el anterior. Android habilita automáticamente Triple Buffering cuando es necesario, pero en iOS el desarrollador puede solicitarlo explícitamente a través de CAMetalLayer. El tercero es Texture Caching: almacenar en caché los mapas de bits en la memoria de la GPU para evitar recargarlos en cada fotograma.
Un ejemplo en Kotlin demuestra la implementación de Frame Pacing con un intervalo fijo de 16.6 ms. Todas las devoluciones de llamada llegan a un intervalo uniforme, incluso si el sistema se retrasa.
class PacedFrameRenderer {
private val targetDelta = 16_666_666L // 16.6 ms (60 FPS)
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
val delta = frameTimeNanos - lastFrameTime
if (delta >= targetDelta) {
onFrame(delta)
lastFrameTime = frameTimeNanos
}
Choreographer.getInstance()
.postFrameCallback(this)
}
private fun onFrame(delta: Long) {
// renderizado de fotograma
}
}
Preguntas frecuentes
60 FPS es un nivel cómodo para las aplicaciones móviles. La diferencia entre 60 y 120 FPS solo se nota en pantallas de alta frecuencia de actualización durante animaciones rápidas (desplazamiento, arrastre). Por debajo de 30 FPS, es incómodo.
FPS = 1000 / FrameTime (ms). Si el tiempo de fotograma es de 16.6 ms, FPS = 60. Si el tiempo de fotograma es de 33.3 ms, FPS = 30. Se recomienda monitorear el tiempo de fotograma en lugar del FPS, ya que muestra los fotogramas problemáticos.
Al desplazarse, el sistema llama a Layout y Draw para cada nuevo elemento de la lista. Si las vistas son complejas, el diseño no se almacena en caché o se utilizan drawables pesados, el tiempo de fotograma aumenta y el FPS cae. La solución es el reciclaje de ViewHolder y una jerarquía plana.
Use Instruments con la plantilla Core Animation (muestra el FPS en tiempo real). Para la medición programática, use CADisplayLink con conteo de fotogramas por segundo. Para producción, use MetricKit con la métrica MXAnimatoryMetric.
Triple Buffering usa tres búferes en lugar de dos, lo que permite que la GPU comience a renderizar el siguiente fotograma antes de que se complete el VSync actual. Esto suaviza las cargas máximas y mejora la estabilidad del FPS, pero agrega un fotograma de latencia.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también