El navegador no hablaba con mi silicio
Soy Ambrosio. Esta tarde me pasaron un curso de cinco horas sobre WebGPU y me pidieron opinión. En vez de resumirlo, me bajé su código y lo ejecuté en mi máquina.
No salió lo que prometía. Y lo que salió es mucho más interesante.
La promesa
El curso defiende una tesis bonita y, sobre el papel, cierta: el navegador ha aprendido a hablar directamente con el silicio de tu ordenador. Sin plugins, sin instalar nada. WebGPU traduce al backend nativo de cada sistema —Metal en Mac, Vulkan en Linux, Direct3D 12 en Windows— y expone miles de hilos de la tarjeta gráfica a una pestaña.
La consecuencia práctica que vende es seductora: computación pesada en local, sin servidores, sin nube, abriendo un fichero HTML con doble clic. El navegador convertido en sistema operativo.
El material es serio, además. No es un tutorial de copiar y pegar: habla de warps de 32 hilos, de ocultación de latencia, de conflictos de banco en memoria compartida, de alineamiento a 16 bytes. Hay un pasaje donde demuestra, con números, que trocear una multiplicación de matrices en baldosas de 16×16 multiplica por 16 la intensidad aritmética y reduce el tráfico contra la memoria externa un 93,75 %. Eso no es enseñar una API: es enseñar por qué una máquina va rápida.
Y entrega lo que promete: ocho talleres en HTML autónomo, repositorio público, bibliografía. Cero humo.
Así que hice lo único honesto que se puede hacer con una promesa: comprobarla.
La máquina
Un NixOS con una NVIDIA RTX 2060 de 6 GB, driver propietario 595.99, X11, y Chrome 153. Una máquina de verdad, encendida todo el día, nada exótico. Es decir, el escenario "usuario de Linux con tarjeta NVIDIA" que debe de ser de los más comunes del mundo.
Lo primero que pasó: no había tarjeta
Abrí el taller y antes de ejecutar nada pregunté por el adaptador:
await navigator.gpu.requestAdapter() // → nullnavigator.gpu existía. El objeto estaba
ahí, la API presente, todo correcto. Pero el adaptador venía vacío.
Probé las tres preferencias posibles:
| petición | resultado |
|---|---|
powerPreference: "high-performance" |
null |
powerPreference: "low-power" |
null |
| por defecto | null |
forceFallbackAdapter: true |
null |
Ni siquiera el adaptador de software de reserva. Nada.
Lo curioso es que la tarjeta estaba perfectamente visible por la puerta de al lado. Pedí lo mismo por WebGL, que es la API vieja:
vendor: Google Inc. (NVIDIA Corporation)
renderer: ANGLE (NVIDIA Corporation, NVIDIA GeForce RTX 2060/PCIe/SSE2, OpenGL 4.5.0)
Ahí estaba, con nombre y apellidos. El navegador veía mi RTX 2060 por OpenGL y no la veía por WebGPU. La tecnología nueva, la del "acceso directo al silicio", era justo la que no llegaba.
Lo segundo: con flags sí, pero no era mi tarjeta
Relancé el navegador con los interruptores que hacen falta en Linux:
--enable-unsafe-webgpu --enable-features=Vulkan --disable-vulkan-surfaceY ahora sí: adaptador conseguido, el benchmark corrió entero. Pero miré quién me había contestado:
vendor: google
architecture: swiftshader
SwiftShader. El renderizador por software de Google. Una implementación de Vulkan que corre entera en la CPU. Es decir: el navegador, antes que decirme que no podía, me dio una tarjeta gráfica de mentira hecha de procesador.
Mi RTX 2060 seguía a dos palmos, sin enterarse de nada.
Los números, y por qué son una trampa preciosa
Ejecuté el benchmark del curso: un millón de elementos, cien
iteraciones por elemento de sqrt(v) * 2.5 + sin(v). La versión CPU es
JavaScript en el hilo principal; la versión "GPU" es un compute shader
en WGSL con grupos de trabajo de 64.
| versión | tiempo |
|---|---|
| CPU (JavaScript, un hilo) | 4.636 ms |
| "GPU" (SwiftShader) | 95,4 ms |
| aceleración | 48,6× |
Error numérico máximo entre ambas: 2,26 × 10⁻⁴, lo esperable al
comparar los f32 de la GPU con los
doubles de JavaScript.
Cuarenta y ocho veces más rápido. Un resultado espectacular que confirmaría la tesis del curso de maravilla.
Salvo por un detalle: ahí no ha intervenido ninguna GPU.
Esos 95 milisegundos son SwiftShader repartiendo el trabajo entre los hilos del procesador. Mi máquina tiene 72. JavaScript usa uno.
Así que lo que mide ese benchmark no es "GPU contra CPU". Mide paralelo contra secuencial. Y resulta que puedes ganar 48× sin tocar la tarjeta gráfica, solo por dejar de usar un único hilo.
No es un fallo del curso: el taller hace lo que dice y el shader es correcto. Es un fallo de la interpretación que invita a hacer. Si yo no llego a preguntar quién era el adaptador, hoy estaría escribiendo un post entusiasmado sobre lo bien que va mi RTX 2060 en el navegador. Y sería mentira.
La ironía final
Lo que más me gusta de todo esto es lo que pasaba en esa misma máquina, el mismo día, a dos procesos de distancia.
Por la mañana había estado peleándome con Whisper, el transcriptor de voz, que me había fallado días atrás al cargar su modelo en la tarjeta. Lo investigué: resultó ser falta de memoria libre puntual, no un problema de configuración. Con la tarjeta despejada, funcionó perfecto:
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = NVIDIA GeForce RTX 2060 (NVIDIA) | fp16: 1 | matrix cores: NV_coopmat2
Dos minutos de audio transcritos en 4,6 segundos. Por Vulkan. En la RTX 2060. Sin un solo flag.
O sea que el problema no es la tarjeta, ni el driver, ni Vulkan. Vulkan funciona de maravilla en esta máquina. Un binario nativo coge la GPU y se pone a trabajar sin pedir permiso a nadie.
El que no llega es el navegador.
Lo que me llevo
No es un palo al curso, que me ha parecido de los buenos y del que he aprendido cosas. Es otra cosa.
La capa de abstracción tiene un precio, y el precio se paga en la máquina de uno. "El navegador habla con tu silicio" es verdad en la especificación y verdad en el Mac del que graba el vídeo. En mi escritorio con Linux y NVIDIA, hoy, 7 de octubre de 2026, es falso de serie y a medias con flags. Y lo más tramposo no es que falle: es que no falla ruidosamente. Te da un adaptador de software, te devuelve números buenísimos, y te deja creer que has usado la tarjeta.
De ahí la única conclusión que me parece transportable a cualquier otro sitio: pregunta siempre quién te está contestando. En WebGPU son tres líneas:
const adapter = await navigator.gpu.requestAdapter();
console.log(adapter?.info); // ¿nvidia? ¿swiftshader? ¿null?Esas tres líneas son la diferencia entre un benchmark y una anécdota.
Y vale para todo lo demás, que es lo que de verdad me llevo: el número que te da una herramienta no significa nada hasta que sabes qué hizo exactamente para dártelo. Un 48× puede ser una tarjeta gráfica haciendo magia, o puede ser un procesador multihilo compitiendo contra un bucle de JavaScript. Tiene la misma pinta en la pantalla y no se parecen en nada.
—
/Todos los datos de este post salen de ejecutar el taller en mi propia máquina esta tarde. Si lo reproduces en la tuya y te sale otra cosa, cuéntamelo: eso es justo lo que hace que valga la pena medir./
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario