Skip to content

Cómo crear un mundo abierto en el navegador, parte 2: físicas en un Worker y el temor al retardo de entrada

Por Oleg Sidorkin, CTO y cofundador de Cinevva

¿Acabas de llegar? Consulta la guía de la serie. Explica qué es un spike y enlaza todas las partes.

Si desarrollas multijugador para navegador durante suficiente tiempo, tarde o temprano surge este debate.

«Ejecutar las físicas en un worker ofrece una arquitectura limpia. Ejecutarlas en el hilo principal parece más seguro».

Ambas afirmaciones pueden ser ciertas. Lo importante es cómo se sienten los controles y cuál es la latencia con entradas reales.

Creamos el Spike 2 para responder a esa cuestión con mediciones, no con opiniones.

Abrir el Spike 2 en una pestaña nueva ↗ · Ver el código fuente

Reutilizamos el terreno del Spike 1 e integramos Rapier en un worker de módulo dedicado. Enviábamos el estado de entrada al worker en cada fotograma, ejecutábamos allí el paso de simulación y devolvíamos la posición autoritativa al renderizador.

Las métricas clave fueron la latencia entre la entrada y el movimiento visible, así como el tiempo de cada paso de físicas. También observamos si aparecían tirones durante el movimiento normal, las aceleraciones al esprintar y la cadencia de los saltos.

El resultado fue mejor de lo esperado. Con la estructura y la cadencia de nuestros mensajes, el límite entre hilos no fue el factor dominante de la latencia. Los controles seguían respondiendo de inmediato, que era lo único que les importaría a los jugadores.

Uno de los desafíos de esta fase fue el riesgo de interpretar mal los resultados. Tras un resultado satisfactorio, los equipos suelen generalizar en exceso y dar por zanjada para siempre la cuestión arquitectónica. No es así. Solo validamos un escenario concreto y un perfil de hardware. Los spikes posteriores aún debían volver a comprobar esas suposiciones cuando cambiara la carga sobre la GPU y el streaming.

Este spike también mejoró nuestro proceso. Empezamos a mostrar de forma predeterminada la telemetría de tiempos en el HUD de los spikes interactivos. Eso hizo que las conversaciones del equipo pasaran de «algo no se siente bien» a «esta ruta añadió 1,2 ms».

En la parte 3 abordamos los experimentos menos vistosos que evitaron costosas sorpresas más adelante: carga de difusión, limitaciones de los dispositivos móviles y fiabilidad de la generación de comportamientos.

Tecnología mencionada en este capítulo

Rapier. Un motor de físicas escrito en Rust que se compila a WebAssembly para utilizarse en el navegador. Gestiona cuerpos rígidos, colisionadores, articulaciones, controladores de personajes y lanzamiento de rayos con un rendimiento entre 2 y 3 veces superior al código nativo. Para los mundos abiertos, Rapier proporciona controladores para los personajes de los jugadores (caminar sobre el terreno, subir escalones y deslizarse por pendientes), colisiones de objetos, lanzamiento de rayos para las interacciones y volúmenes de activación. Consulta la documentación de Rapier y nuestra guía tecnológica de 3D para navegador sobre físicas.

Web Workers. Hilos del navegador que ejecutan JavaScript (o Wasm) fuera del hilo principal. Ejecutar la simulación de físicas en un worker significa que una llamada pesada a world.step() no bloquea el renderizado. El hilo principal envía el estado de entrada al worker en cada fotograma mediante postMessage y recibe a cambio las posiciones autoritativas. La penalización de latencia corresponde a los dos saltos de los mensajes (aproximadamente entre 0,1 y 0,5 ms cada uno en equipos de escritorio). La ventaja es que el hilo de renderizado nunca se bloquea al detectar colisiones. Los objetos transferibles (transferencia de ArrayBuffer) eliminan el coste de las copias para matrices de posiciones grandes.

WebAssembly (Wasm). Un formato de instrucciones binarias que se ejecuta en los navegadores a una velocidad cercana a la nativa. Rapier, Havok y Recast se compilan a Wasm. En Rapier-Wasm, el paso de físicas suele tardar entre 0,5 y 2 ms para unos cientos de cuerpos, frente a los 5-15 ms de una implementación equivalente en JavaScript. Los módulos Wasm se cargan como archivos .wasm obtenidos junto con el código de enlace de JavaScript. Consulta la especificación de WebAssembly.

Latencia entre la entrada y la imagen. El tiempo transcurrido entre una pulsación de tecla y el cambio visual resultante en pantalla. Para que el movimiento se perciba como «inmediato», debe mantenerse por debajo de unos 80 ms. En una configuración con las físicas ejecutándose en un worker, los tiempos de la cadena se suman:

L=tinput+2tmsg+tstep+trender+tvsync

Evento keydown (hilo principal), postMessage al worker y de vuelta (2tmsg), paso de físicas, aplicación de la nueva posición por parte del renderizador y, por último, la siguiente sincronización vertical. Cada término es pequeño por sí solo (aproximadamente entre 0,1 y 0,5 ms por salto de mensaje en equipos de escritorio), pero se acumulan; por eso medimos L directamente en lugar de confiar en el diagrama de arquitectura.


Parte 2 de 12.
Anterior: Parte 1: Empezamos intentando romperlo
Siguiente: Parte 3: Los spikes poco vistosos que nos salvaron
Guía de la serie: /es/blog/2026-02-25-open-world-browser-series-guide