- Como GitHub Pages no soporta Brotli, este es un experimento para codificar HTML como una imagen WebP sin pérdida y restaurarlo con el decodificador de imágenes del navegador y JavaScript, reduciendo así la cantidad de datos transferidos
- Un decodificador Brotli en WASM agrega un costo extra de
71 KiB~200 KB, y la Compression Streams API solo soportagzip,deflate,deflate-raw, por lo que es difícil usarla como vía alternativa para decodificar Brotli - WebP sin pérdida VP8L puede producir resultados más pequeños que gzip incluso con datos de texto, gracias a la transformación predictiva, la reutilización de árboles Huffman por bloques de 16x16 y el color cache
- El HTML de prueba de
439,478bytes se redujo a94,683bytes con gzip y hasta43,182bytes con WebP; seguía siendo más grande que Brotli con37 KiB, pero era alrededor de 2.2 veces más pequeño que gzip - Por el ruido anti-fingerprinting de Canvas 2D, los cambios en
readPixelsde WebGL, la pantalla vacía inicial y los problemas al restaurar el scroll, esto se parece más a un hack que expone las limitaciones del navegador que a algo aplicable en producción
El problema de no poder usar Brotli en GitHub Pages
- Al reducir el tiempo de carga de una página, la compresión HTTP tiene más impacto que minificar el HTML
- HTTP soporta gzip y Brotli mediante el encabezado
Content-Encoding- gzip es barato de usar, así que suele venir activado por defecto
- Brotli normalmente comprime mejor que gzip, pero es mucho más lento
- GitHub Pages no soporta Brotli, así que el artículo más largo del sitio,
Recovering garbled Bitcoin addresses, termina pesando92 KiBcon gzip en vez de los37 KiBque podría tener con Brotli - Esa diferencia hace que el tiempo de carga aumente innecesariamente en 2.5 veces
La primera alternativa pensada y dónde se atoró
- Si GitHub permitiera subir y servir archivos Brotli precomprimidos, el problema podría evitarse, pero esa función no existe
- La opción de descomprimir directamente en el cliente con JavaScript pierde ventaja por el tamaño del decodificador WASM
- brotli-dec-wasm pesa alrededor de
200 KB - tiny-brotli-dec-wasm pesa
71 KiB - La comparación termina siendo entre gzip
92 KiBy un cuerpo Brotli de37 KiB + 71 KiB, así que el beneficio desaparece
- brotli-dec-wasm pesa alrededor de
- El stack HTTP del navegador sí tiene un decodificador Brotli, pero DecompressionStream de la Compression Streams API solo acepta
gzip,deflate,deflate-raw - Incluso precomprimiendo
gzipcon Zopfli, queda en86 KiB, así que sigue siendo más grande que Brotli
Usar un formato de imagen como contenedor de compresión
- Como el navegador ya puede decodificar imágenes, se pueden meter los datos en los píxeles de una imagen y volver a leerlos con la Canvas API, sin agregar una nueva lógica de descompresión
- GIF acomoda los datos en orden row-major y luego aplica LZW, pero DEFLATE de gzip fue diseñado justamente para reemplazar LZW, así que es difícil esperar una ganancia
- PNG usa DEFLATE, pero antes aplica una transformación predictiva que comprime no los píxeles originales, sino sus diferencias con los píxeles vecinos
- Ejemplo: comprimir
[a, b, c, d]en lugar de[a, b-a, c-b, d-c] - Cuanto menor sea la diferencia entre el valor predicho y el real, más favorable resulta para la compresión Huffman
- Ejemplo: comprimir
- El núcleo del experimento es usar VP8L, la variante sin pérdida de WebP, para comprimir datos de bytes genéricos
En qué se diferencia VP8L de gzip
- WebP tiene variantes con pérdida y sin pérdida; aquí solo se trata el formato sin pérdida VP8L
- VP8L usa transformación predictiva como PNG, pero en vez de DEFLATE usa un esquema parecido a DEFLATE creado por Google
- DEFLATE puede dividir un archivo en varios fragmentos y usar un árbol Huffman adecuado para cada uno
- Si en un mismo HTML se mezclan JavaScript, SVG y marcado, pueden convenir árboles distintos
- VP8L puede definir una tabla de árboles Huffman arbitrariamente grande y usar un árbol distinto para cada bloque de 16x16 píxeles
- Si después de JavaScript viene CSS y luego otra vez JavaScript, DEFLATE puede terminar codificando varias veces árboles parecidos
- VP8L puede reutilizar árboles, así que puede cambiarlos con más frecuencia y a menor costo
- El color cache de VP8L permite representar valores de forma corta, como “copiar un píxel reciente con ciertas propiedades”
Primer experimento de compresión con WebP
- El archivo de prueba fue el HTML de
Recovering garbled Bitcoin addresses- Tamaño original:
439,478bytes gzip --best:94,683bytes
- Tamaño original:
- Con el crate webp de Rust, los bytes se convirtieron en una imagen RGB en escala de grises y luego se comprimieron como WebP sin pérdida
- Se usó escala de grises por la transformación
subtract greende WebP- En escala de grises, si se resta el canal G de R/B, R/B quedan efectivamente en 0
- WebP codifica los tres canales con árboles Huffman separados, así que un canal de valor fijo ocupa casi espacio
O(1)
- Al principio se intentó crear una imagen
1xN, pero WebP solo soporta hasta16383x16383, así que apareció el errorVP8_ENC_ERROR_BAD_DIMENSION - Al ajustarlo a un formato
16383xN, el resultado quedó en45,604bytes, 2 veces más pequeño que gzip y más pequeño incluso que bzip2 con49,764bytes
Ajustes específicos para WebP
- Si se usa orden row-major en una imagen ancha, dentro de un bloque de 16x16 se mezclan bytes que en la entrada original estaban muy alejados
- Al cambiar la forma de la imagen a una más alta y angosta,
27x16383, el resultado de compresión bajó a43,232bytes - Se comparó la opción de rendimiento de compresión method de
cwebpentre0~6- method 0:
48,902 - method 1:
43,546 - method 2:
43,442 - method 3:
43,292 - method 4:
43,232 - method 5:
43,182 - method 6:
43,182
- method 0:
- Se eligió method
5porque daba el mismo tamaño que method6, pero era más rápido - En ese estado, WebP era 2.2 veces más pequeño que gzip y 1.2 veces más grande que Brotli
Benchmark con varios archivos
- Los datos de comparación fueron snappy testdata, Canterbury Corpus y Large Corpus y 2 archivos SVG
- Los formatos comparados fueron
gzip --best,brotli --best,bzip2 --besty el script de compresión con WebP - Salvo en archivos muy pequeños como
grammar.lsp,xargs.1y unas pocas excepciones, WebP casi siempre superó a gzip - Las excepciones fueron
kennedy.xlsypaper-100k.pdfpaper-100k.pdftiene19 KBde XML y luego datos comprimidos, así que en la práctica se estaba midiendo una porción pequeña de datoskennedy.xlstambién muestra un rendimiento extraño frente a Brotli y bzip2, y puede ser un archivo con muchos datos heterogéneos cercanos entre sí, difícil para el compresor
- WebP tendía a rendir un poco peor que bzip2, aunque en algunos casos lo superaba
- WebP siempre fue peor que Brotli, salvo excepciones como
fireworks.jpeg, que se parece más a un blob aleatorio casi uniforme - En datos grandes de texto plano sí mostró mejoras medibles frente a gzip
- También hubo mejoras en los archivos SVG
- En
html_x_4, WebP logró una tasa de compresión de3.3%; peor que Brotli con2.8%, pero muy superior a gzip con13%
Restaurarlo con JavaScript
- La decodificación de WebP se puede implementar con
fetch,createImageBitmap,OffscreenCanvas,getImageDatayTextDecoder - La idea es usar el canal R de los píxeles como bytes del HTML original, decodificarlos como UTF-8 y ponerlos en
document.documentElement.innerHTML - La Canvas API se usa con frecuencia para fingerprinting, así que algunos navegadores agregan ruido al resultado de
getImageData- En Firefox con strict tracking protection, menos del 1% de los píxeles puede verse afectado
- En el HTML, ese ruido aparece como si fueran errores tipográficos
- Usando
readPixelsde WebGL, en ese momento funcionaba sin ruido- WebGL solo soporta texturas de hasta
2048x2048de forma confiable, así que hubo que volver a ajustar el límite de tamaño - Este código de restauración, tras minificarlo, pesaba cerca de
550bytes
- WebGL solo soporta texturas de hasta
- Sumando WebP y el código, el total quedaba en
44 KiB, frente a gzip con92 KiBy Brotli con37 KiB - Después del 15 de abril de 2026, Firefox añadió protección anti-fingerprinting también a
readPixels, así que este método ya no funciona tal cual
Parpadeo de pantalla y problema del scroll
- Como
awaitse resuelve sobre promesas, el navegador considera que la ejecución del script terminó antes de que termine la descarga del WebP - Como el DOM sigue vacío, el usuario ve por un momento una pantalla blanca vacía
- Como mitigación, se pueden dejar en el HTML con gzip los estilos y unos
8 KiBde la parte superior de la página, y comprimir con WebP solo el contenido bajo el viewport - Restaurar la posición del scroll al recargar también causa problemas
- Por ejemplo, si se recarga en la posición
Y = 5000pxpero la altura de la página es0px, la posición se reinicia - Agregar un
divtemporal muy grande ayuda
- Por ejemplo, si se recarga en la posición
- En vez de
document.write, hay que asignar adocument.documentElement.innerHTMLpara actualizar el documento actual sin reemplazarlo por uno nuevo
Meter WebP directamente dentro de JavaScript
- Para reducir un poco más la latencia, WebP se puede incrustar directamente dentro de JavaScript
- La forma más simple es usar un data URL en base64
- base64 aumenta el tamaño original en
1.33 veces, pero gzip compensa casi por completo ese aumento- Si
compressed.webpse convierte a base64, queda en57,576bytes - Si luego se comprime con gzip
--best, queda en43,519bytes
- Si
- Un blob comprimido como WebP se parece a datos aleatorios casi uniformes, y el paso de 8 bits a 6 bits de base64 hace que el árbol Huffman de gzip actúe casi como una transformación inversa
- También se podría usar Unicode y UTF-16, pero base64 sigue siendo una primera solución suficientemente buena
Aplicación real y estado posterior
- Al momento de escribirse el texto, esta misma página estaba comprimida con WebP a partir de la sección “Fool me twice”, salvo en navegadores viejos o con JavaScript desactivado
- La imagen WebP de la página real era alta y angosta, aunque también se mostraba un ejemplo cuadrado de WebP por estética
- En la imagen, la parte superior e inferior claras correspondían al texto y al código; la zona rayada cerca de 1/5 de la altura era un diagrama, y la mayor parte de las zonas oscuras correspondían al texto dentro del diagrama
- La reducción real fue limitada
- Página original con gzip:
88 KiB - Página con WebP aplicado y gzip:
83 KiB - Estimación con Brotli:
69 KiB
- Página original con gzip:
- Después del 15 de abril de 2026, para que los visitantes de Firefox no vieran contenido roto, la página volvió a una versión sin esta compresión
- El código en Rust, el corpus y otros archivos están publicados en GitHub
1 comentarios
Opiniones de Hacker News
Si ignoras la latencia, sí, pero en la práctica parece que el tiempo de carga aumenta alrededor de 0.001%
El aumento de tamaño no es significativo comparado con la latencia de ida y vuelta, y puede que el tiempo que toma descomprimir sea mayor que el tiempo ahorrado por transmitir 55 KiB menos
Aunque es un experimento interesante, en este caso es muy probable que la experiencia de usuario empeore; la velocidad sería casi la misma y solo se perdería compatibilidad
El problema es que hay gente como el autor de TFA que se esfuerza por reducir 100 KB a 50 KB, pero también hay sitios que, sin pensarlo, me envían decenas de MB en imágenes cuando solo quiero ver el horario de un restaurante usando datos en roaming
La conciencia sobre los recursos existe, pero por desgracia está distribuida de forma muy desigual
Si se corta la conexión, lo pierdes todo, y ni siquiera puedes leer la parte descargada
En conexiones normales la diferencia no es significativa, y en conexiones tan lentas o inestables como para que 50 KB importen, este método definitivamente empeora las cosas. Es un experimento interesante, pero preferiría que no lo aplicaran a sitios
Symbols-2048-em%20Nerd%20Font%20Complete.woff2de 850K, así que eso prácticamente tapa la diferenciaNo será una diferencia de 2.5 veces, pero tampoco es 0.001%
En redes con pérdida podría notarse, pero no estoy seguro
No sé por qué
readPixelsno es objetivo de las protecciones contra fingerprinting. Como no esparce erratas casi invisibles por toda la página, para mí está bienLa parte que dice que en el HTML comprimido con gzip solo quedan los estilos y unos 8 KiB de la parte superior de la página, y que solo el contenido debajo del viewport se comprime con WebP, explica por qué el texto se cortaba de golpe después de una frase cualquiera y seguía una página en blanco
Uso LibreWolf, así que WebGL está desactivado, y para juegos web aleatorios que necesitan WebGL uso Chromium. Al activar WebGL, el artículo funcionó bien y, sinceramente, es una técnica bastante limpia
La WWW debería ser universalmente accesible partiendo de HTML común y corriente y mejorando progresivamente
También es posible usar Brotli directamente en el navegador web, aunque por supuesto tiene limitaciones
Creo que una propuesta de JS1024 de 2022 [1] fue la primera demostración de este concepto, y también hay código de prueba de concepto para compresión arbitraria. Lamentablemente, no servía para su propósito original, que era code golf de tamaño
La limitación clave es que en la práctica está restringido a caracteres ASCII y, por razones obvias, es muy sensible al stack de renderizado. Ahora mismo parece que no funciona en Firefox
[1] https://js1024.fun/demos/2022/18/readme
[2] https://gist.github.com/lifthrasiir/1c7f9c5a421ad39c1af19a9c...
Solo se puede usar el formato de archivo de fuentes WOFF2, para el que Brotli fue diseñado originalmente, pero para aprovecharlo hay que crear un archivo de fuente completo
Los navegadores recientes suelen limpiar las fuentes con OpenType Sanitizer (OTS), porque poner directamente archivos de fuentes no confiables en el sistema es muy peligroso; por eso hay que crear un archivo WOFF2 que sea lo bastante normal como para que OTS lo acepte, pero que aun así contenga y permita extraer la secuencia de bytes deseada
Tras muchos fracasos, la idea de terminar usando los anchos de glifo, es decir, los avances, que se codifican como una secuencia de enteros con signo de 2 bytes casi sin restricciones, es fantástica
Chromium lo estuvo bloqueando durante mucho tiempo, pero zstd ya está llegando a la web. Por fin llegó a Chrome, así que ahora solo falta que Safari se ponga al día
Estoy creando Batch Compress (https://batchcompress.com/en) y, poco después de agregar soporte para WebP, lo cambié a la opción predeterminada.
Hasta donde sé, ya estaba generando los JPEG más pequeños entre las herramientas de compresión web, pero WebP terminaba pesando apenas alrededor del 50% de un JPEG. Tras agregar el soporte, pasarlo a predeterminado fue una decisión fácil.
Como el sitio tiene bastantes usuarios, pensé que habría algunas quejas después de cambiar WebP a predeterminado, pero ya pasó más o menos un mes y solo hubo una consulta o queja relacionada con WebP.
Parece que ahora casi todas las herramientas y navegadores soportan WebP. Hace poco vi un sitio web donde no se manejaban bien las subidas de imágenes WebP y eso bloqueaba el siguiente paso, pero hoy en día casi todo lo soporta bien.
Si se comprime y optimiza bien un JPEG, no debería quedar muy por detrás de WebP.
Siempre se puede crear un WebP que se vea casi igual que un JPEG y reducir el tamaño de archivo, pero lo mismo pasa si se vuelve a comprimir como un JPEG que se vea casi igual.
Es una característica de todos los códecs con pérdida y, como el tamaño de archivo crece exponencialmente a medida que sube la calidad, a la gente siempre le sorprende cuánto puede cambiar el tamaño con una degradación de calidad muy pequeña, casi imperceptible.
WebP tenía mala fama por sus valores predeterminados terribles, que arruinaban los detalles en las zonas oscuras.
Revisando el código fuente, vi que a la declaración doctype le falta un espacio. La forma actual es incorrecta; debería llevar un espacio.
Omitir el espacio en
DOCTYPEpuede hacerlo más corto.Estrictamente hablando, no es HTML válido, pero aun así activa correctamente el modo estándar.
Referencia: https://GitHub.com/kangax/html-minifier/pull/970 / https://HTML.spec.WHATWG.org/multipage/parsing.html#parse-er...
Yo también uso ese truco en https://FreeSolitaire.win.
Parece el resultado de un minifier que elimina todo lo posible [0]
0: https://github.com/KTibow/KTibow/issues/3#issuecomment-23367...
Probé este truco hace tiempo. Curiosamente no recuerdo para qué lo usé, pero probablemente era para ver si se podía hacer, y también dejé un comentario aquí: https://gist.github.com/gasman/2560551?permalink_comment_id=...
También encontré un prototipo antiguo, que parece haber sido solo una prueba: https://retr0.id/stuff/bee_movie.webp.html
Hoy se les dice extensiones, no add-ons, a esas cosas que inyectan scripts, pero en fin: me parece interesante el enfoque de pasar “basura” al principio y luego pegar JS detrás para devolvérselo a la página.
El fanático de seguridad que llevo dentro se pregunta si esto podría habilitar un ataque cuando hay datos proporcionados por usuarios, como un formulario de comentarios.
Me pregunto si alguien podría encontrar una secuencia de bytes para poner en un comentario que, después de la compresión, se convierta en una etiqueta
scriptubicada antes de mi script y termine ejecutándose.La mayor parte de lo que WebP lossless aporta sobre PNG tiene que ver con el modelado, no con la codificación, y esta compresión de texto usa solo la parte de codificación de WebP.
Quitar Google Fonts también mejoraría un poco el tiempo de carga de la página, porque se cargan desde un servidor remoto y requieren handshakes adicionales.
Esta página se rompe al menos en el navegador de Sailfish OS. Hay un espacio vacío largo después del siguiente párrafo:
“Alright, so we’re dealing with 92 KiB for gzip vs 37 + 71 KiB for Brotli. Umm…”
Aun así, el overhead de comprimir HTML con gzip y Brotli no es nada comparado con la cantidad de JS, imágenes y video que usan los sitios web actuales.
Personalmente no me gusta mucho este formato. Cuando guardo una imagen y se guarda como WebP, casi nada lo soporta fuera de los navegadores web, así que tengo que convertirla antes de editarla o usarla de forma significativa.
Se siente como si simplemente me obligara a dar un paso extra.
Aun así, si el soporte sigue aumentando, me parece bien. Puedo tolerar que aparezca un formato nuevo cada 20 años.
.webmsí puede desaparecer.