1 poin oleh GN⁺ 2024-09-08 | 1 komentar | Bagikan ke WhatsApp
  • Ini adalah eksperimen untuk mengurangi jumlah data yang ditransfer ketika GitHub Pages tidak mendukung Brotli, dengan mengenkode HTML sebagai gambar WebP lossless lalu memulihkannya menggunakan image decoder browser dan JavaScript
  • Decoder WASM Brotli membutuhkan biaya tambahan 71 KiB~200 KB, sementara Compression Streams API hanya mendukung gzip, deflate, dan deflate-raw, sehingga sulit menjadi jalan pintas untuk decoding Brotli
  • VP8L WebP lossless dapat menghasilkan ukuran lebih kecil daripada gzip bahkan untuk data yang bersifat teks, berkat predictive transform, penggunaan ulang Huffman tree per blok 16x16, dan color cache
  • HTML uji 439,478 byte menyusut menjadi gzip 94,683 byte dan WebP 43,182 byte; masih lebih besar daripada Brotli 37 KiB, tetapi sekitar 2,2 kali lebih kecil dibanding gzip
  • Karena noise anti-fingerprinting pada Canvas 2D, perubahan WebGL readPixels, layar kosong awal, dan masalah pemulihan scroll, ini lebih dekat ke hack yang mengungkap batasan browser daripada sesuatu yang layak diterapkan sungguhan

Masalah tidak bisa memakai Brotli di GitHub Pages

  • Saat mengurangi waktu muat halaman, kompresi HTTP memberi efek lebih besar daripada minify HTML
  • HTTP mendukung gzip dan Brotli melalui header Content-Encoding
    • gzip murah sehingga biasanya aktif secara default
    • Brotli umumnya memiliki rasio kompresi lebih baik daripada gzip, tetapi jauh lebih lambat
  • GitHub Pages tidak mendukung Brotli, sehingga tulisan terpanjang di situs, Recovering garbled Bitcoin addresses, menjadi 92 KiB dengan gzip, padahal bisa 37 KiB jika memakai Brotli
  • Karena perbedaan ini, waktu muat menjadi 2,5 kali lebih lama secara tidak perlu

Alternatif pertama yang terpikir dan titik buntu

  • Jika GitHub mendukung unggah dan penyajian file Brotli yang sudah dikompresi sebelumnya, masalah ini bisa dihindari, tetapi fitur seperti itu tidak disediakan
  • Opsi melakukan dekompresi langsung di sisi klien dengan JavaScript kehilangan keunggulannya karena ukuran decoder WASM
  • Stack HTTP browser memiliki decoder Brotli, tetapi DecompressionStream dari Compression Streams API hanya menerima gzip, deflate, dan deflate-raw
  • Walaupun gzip dikompresi sebelumnya dengan Zopfli, hasilnya 86 KiB, tetap lebih besar daripada Brotli

Memakai format gambar seperti container kompresi

  • Browser dapat mendecode gambar, jadi jika data dimasukkan ke piksel gambar lalu dibaca kembali dengan Canvas API, tidak perlu menambahkan logika dekompresi baru
  • GIF membentangkan data dalam urutan row-major lalu menerapkan LZW, tetapi sulit mengharapkan keuntungan karena DEFLATE pada gzip dirancang untuk menggantikan LZW
  • PNG memakai DEFLATE, tetapi lebih dulu menerapkan predictive transform yang mengompresi selisih terhadap piksel tetangga, bukan sumber piksel mentah
    • Contoh: mengompresi [a, b, c, d] bukan sebagai itu, melainkan [a, b-a, c-b, d-c]
    • Semakin kecil selisih antara nilai prediksi dan nilai sebenarnya, semakin menguntungkan untuk kompresi Huffman
  • Inti eksperimen ini adalah memanfaatkan VP8L, yaitu WebP lossless, untuk kompresi data byte umum

Perbedaan VP8L dengan gzip

  • WebP memiliki varian lossy dan lossless; di sini hanya dibahas VP8L, format lossless
  • VP8L memakai predictive transform seperti PNG, tetapi alih-alih DEFLATE, ia memakai metode mirip DEFLATE buatan Google
  • DEFLATE dapat membagi file menjadi beberapa potongan dan menaruh Huffman tree yang sesuai untuk tiap potongan
    • Jika JavaScript, SVG, dan markup bercampur dalam satu HTML, tree yang berbeda dapat lebih menguntungkan
  • VP8L mendefinisikan tabel Huffman tree yang ukurannya dapat dibuat sangat besar, lalu tiap blok piksel 16x16 dapat memakai tree berbeda
    • Jika setelah JavaScript muncul CSS lalu JavaScript lagi, DEFLATE dapat mengenkode tree yang mirip berkali-kali
    • VP8L dapat menggunakan ulang tree, sehingga bisa mengganti tree lebih sering dan lebih murah
  • Color cache VP8L dapat merepresentasikan nilai secara singkat, misalnya dengan instruksi untuk menyalin piksel terbaru yang memiliki atribut tertentu

Eksperimen kompresi WebP pertama

  • File uji adalah HTML Recovering garbled Bitcoin addresses
    • Ukuran asli: 439,478 byte
    • gzip --best: 94,683 byte
  • Dengan crate webp di Rust, byte diubah menjadi gambar RGB grayscale lalu dikompresi menjadi WebP lossless
  • Alasan memakai grayscale adalah transform subtract green pada WebP
    • Pada grayscale, jika kanal G dikurangkan dari R/B, R/B pada dasarnya menjadi 0
    • Karena WebP mengenkode tiga kanal dengan Huffman tree terpisah, kanal bernilai tetap mendekati ruang O(1)
  • Awalnya gambar hendak dibuat sebagai 1xN, tetapi WebP hanya mendukung maksimum 16383x16383, sehingga muncul error VP8_ENC_ERROR_BAD_DIMENSION
  • Setelah disesuaikan menjadi bentuk 16383xN, hasilnya 45,604 byte, 2 kali lebih kecil daripada gzip dan bahkan lebih kecil daripada bzip2 49,764 byte

Penyesuaian khusus WebP

  • Jika gambar lebar memakai urutan row-major, byte yang berjauhan dalam input bercampur dalam blok 16x16
  • Mengubah bentuk gambar menjadi tinggi dan sempit, 27x16383, menurunkan hasil kompresi menjadi 43,232 byte
  • Pengaturan performa kompresi cwebp, yaitu method, dibandingkan dari 0~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 5 dipilih sebagai opsi yang lebih cepat sambil menghasilkan ukuran yang sama dengan method 6
  • Dalam keadaan ini, WebP 2,2 kali lebih kecil daripada gzip dan 1,2 kali lebih besar daripada Brotli

Benchmark beberapa file

  • Objek perbandingan adalah snappy testdata, Canterbury Corpus dan Large Corpus, serta 2 file SVG
  • Format yang dibandingkan adalah gzip --best, brotli --best, bzip2 --best, dan skrip kompresi WebP
  • WebP hampir selalu lebih baik daripada gzip, kecuali file yang sangat kecil seperti grammar.lsp, xargs.1, dan beberapa pengecualian
  • Pengecualiannya adalah kennedy.xls dan paper-100k.pdf
    • paper-100k.pdf memiliki data terkompresi setelah XML 19 KB, sehingga pada dasarnya situasinya seperti mengukur data kecil
    • Performa relatif Brotli/bzip2 pada kennedy.xls juga aneh, dan bisa jadi ini file dengan banyak data heterogen di lokasi berdekatan sehingga sulit ditangani kompresor
  • WebP cenderung sedikit lebih buruk daripada bzip2, tetapi dalam beberapa kasus mengunggulinya
  • WebP selalu lebih buruk daripada Brotli, kecuali pengecualian yang hampir menyerupai blob acak seragam seperti fireworks.jpeg
  • Pada data plain-text besar, WebP menghasilkan peningkatan yang terukur dibanding gzip
    • Ada peningkatan juga pada file SVG
    • Pada html_x_4, WebP mencatat rasio kompresi 3,3%; masih lebih buruk daripada Brotli 2,8%, tetapi jauh lebih baik daripada gzip 13%

Memulihkan dengan JavaScript

  • Decoding WebP sendiri dapat diimplementasikan dengan fetch, createImageBitmap, OffscreenCanvas, getImageData, dan TextDecoder
  • Strukturnya memakai kanal R piksel sebagai byte HTML asli, mendecodenya sebagai UTF-8, lalu memasukkannya ke document.documentElement.innerHTML
  • Karena Canvas API sering dipakai untuk fingerprinting, sebagian browser menambahkan noise pada hasil getImageData
    • Pada strict tracking protection di Firefox, kurang dari 1% piksel dapat terdampak
    • Dalam HTML, noise ini muncul seperti typo
  • Jika memakai readPixels dari WebGL, saat itu dapat berjalan tanpa noise
    • WebGL hanya mendukung tekstur secara stabil hingga 2048x2048, sehingga batas ukuran harus disesuaikan lagi
    • Kode pemulihan ini sekitar 550 byte setelah minify
  • Jika WebP dan kode digabung, ukurannya menjadi 44 KiB, dibandingkan dengan gzip 92 KiB dan Brotli 37 KiB
  • Setelah 15 April 2026, Firefox menambahkan anti-fingerprinting juga pada readPixels, sehingga cara ini tidak lagi bekerja apa adanya

Kedipan layar dan masalah scroll

  • Karena await diproses berbasis promise, browser menganggap eksekusi skrip sudah selesai sebelum unduhan WebP selesai
  • DOM masih kosong, sehingga pengguna melihat layar putih kosong sebentar
  • Sebagai mitigasi, style dan sekitar 8 KiB bagian atas halaman dapat tetap berada di HTML gzip, sementara hanya konten di bawah viewport yang dikompresi sebagai WebP
  • Pemulihan posisi scroll saat refresh juga menjadi masalah
    • Misalnya, jika refresh dilakukan pada posisi Y = 5000px tetapi tinggi halaman 0px, posisi akan direset
    • Menambahkan div sementara yang sangat besar dapat membantu
  • Agar dokumen saat ini dapat diperbarui tanpa diganti menjadi dokumen baru, harus menetapkan document.documentElement.innerHTML, bukan memakai document.write

Memasukkan WebP langsung ke JavaScript

  • Untuk sedikit mengurangi latensi, WebP dapat di-embed langsung di dalam JavaScript
  • Cara paling sederhana adalah base64 data URL
  • base64 memperbesar ukuran asli menjadi 1,33 kali, tetapi gzip hampir sepenuhnya meniadakan kenaikan ini
    • Jika compressed.webp dijadikan base64, ukurannya 57,576 byte
    • Jika ini dikompresi dengan gzip --best, hasilnya 43,519 byte
  • Blob terkompresi seperti WebP hampir berupa data acak seragam, dan terhadap hasil konversi 8-bit-ke-6-bit base64, Huffman tree milik gzip pada dasarnya bekerja seperti transformasi balik
  • Unicode dan UTF-16 juga bisa digunakan, tetapi base64 tetap menjadi solusi awal yang memadai

Penerapan nyata dan kondisi setelahnya

  • Pada saat tulisan ini dibuat, halaman ini sendiri dikompresi dengan WebP mulai dari bagian “Fool me twice”, kecuali di browser lama atau lingkungan dengan JavaScript nonaktif
  • Gambar WebP pada halaman sebenarnya tinggi dan sempit dalam kode sungguhan, tetapi contoh WebP berbentuk persegi juga disediakan agar enak dilihat
  • Dalam gambar, bagian atas dan bawah yang terang adalah teks dan kode, area bergaris sekitar 1/5 dari atas adalah diagram, dan sebagian besar area gelap adalah teks di dalam diagram
  • Penghematan sebenarnya terbatas
    • Halaman gzip lama: 88 KiB
    • Halaman gzip setelah menerapkan WebP: 83 KiB
    • Perkiraan Brotli: 69 KiB
  • Setelah 15 April 2026, agar pengunjung Firefox tidak melihat konten rusak, saat ini halaman diubah kembali menjadi halaman tanpa kompresi
  • Kode Rust, korpus, dan file lainnya tersedia di GitHub

1 komentar

 
GN⁺ 2024-09-08
Pendapat Hacker News
  • Mungkin benar kalau mengabaikan latensi, tetapi dalam praktiknya waktu muat tampaknya hanya bertambah sekitar 0,001%
    Kenaikan ukuran itu tidak berarti dibanding latensi bolak-balik, dan waktu yang dihabiskan untuk dekompresi bisa saja lebih besar daripada waktu yang dihemat dengan mengirim 55KiB lebih sedikit
    Meski ini eksperimen yang menarik, dalam kasus ini pengalaman pengguna kemungkinan besar justru memburuk, dan kecepatannya hampir sama sementara kompatibilitasnya saja yang menurun

    • Kalau hanya mengoptimalkan waktu muat dan mengasumsikan kecepatan data semua orang, itu benar. Namun sering kali saya tidak ingin pembuat situs web atau aplikasi terlalu mudah memutuskan kompromi kecepatan/data atas nama saya
      Masalahnya, ada orang seperti penulis TFA yang berusaha mengecilkan 100KB menjadi 50KB, tetapi ada juga tempat yang tanpa pikir panjang mengirim puluhan MB gambar kepada saya saat saya hanya ingin melihat jam buka restoran lewat data roaming
      Kesadaran terhadap sumber daya memang ada, tetapi sayangnya penyebarannya sangat tidak merata
    • Ini bukan cuma soal waktu dekompresi. Berkas baru bisa didekompresi setelah seluruhnya diunduh, sedangkan browser bisa langsung mendekompresi dan merender HTML yang di-stream dari server
      Jika koneksi terputus, semuanya hilang, dan bahkan bagian yang sudah diunduh pun tidak bisa dibaca
      Pada koneksi normal perbedaannya tidak berarti, dan pada koneksi yang sangat lambat atau tidak stabil sampai 50KB jadi penting, cara ini jelas lebih buruk. Eksperimen yang menarik, tetapi saya berharap ini tidak diterapkan di situs
    • Jika belum ada di cache, ada file Symbols-2048-em%20Nerd%20Font%20Complete.woff2 berukuran 850K, jadi perbedaannya hampir tertutup olehnya
    • Selisih ukuran sebesar itu cukup besar untuk memengaruhi jumlah round-trip yang dibutuhkan. Dengan nilai initial congestion window modern yang masuk akal, kira-kira seharusnya mengurangi sekitar 1 round-trip
      Memang tidak sampai 2,5 kali berbeda, tetapi juga bukan 0,001%
    • Kalau penghematannya kurang dari satu TCP receive window, seharusnya tidak ada perbedaan pada latensi
      Pada jaringan yang mengalami loss mungkin bisa berbeda, tetapi saya tidak yakin
  • Saya tidak tahu kenapa readPixels bukan target perlindungan fingerprinting. Bagi saya tidak masalah, karena saya tidak menyebarkan typo yang nyaris tak terlihat ke seluruh halaman
    Bagian yang mengatakan bahwa HTML yang di-gzip hanya berisi style dan sekitar 8KiB bagian atas halaman, sementara konten di bawah viewport saja yang dikompresi sebagai WebP, menjelaskan mengapa artikel tiba-tiba terputus setelah kalimat acak lalu diikuti halaman kosong
    Saya memakai LibreWolf, jadi WebGL dimatikan, dan untuk game web acak yang membutuhkan WebGL saya memakai Chromium. Saat WebGL dinyalakan, artikelnya berjalan dengan baik, dan jujur tekniknya cukup rapi

    • Sulit menyebutnya rapi jika tidak berjalan di semua browser web modern, bahkan saat perlindungan fingerprinting aktif, dan tidak ada fallback untuk browser lama
      WWW semestinya dapat diakses secara universal dengan cara yang dimulai dari HTML biasa lalu ditingkatkan secara progresif
  • Menggunakan Brotli secara langsung di browser web memang memungkinkan, tetapi tentu ada batasannya
    Saya rasa karya kiriman JS1024 tahun 2022 [1] adalah demonstrasi pertama konsep ini, dan ada juga kode proof-of-concept untuk kompresi arbitrer. Sayangnya, itu tidak cocok untuk size coding yang merupakan tujuan awalnya
    Batasan utamanya adalah pada dasarnya terbatas pada karakter ASCII, dan karena alasan yang jelas sangat sensitif terhadap stack rendering. Saat ini tampaknya tidak berjalan di Firefox

    [1] https://js1024.fun/demos/2022/18/readme

    [2] https://gist.github.com/lifthrasiir/1c7f9c5a421ad39c1af19a9c...

    • Kunci untuk memahami pendekatan ini adalah bagian berikut, tanpa perlu menggali sampai detail bagaimana semuanya dipaksakan masuk
      Yang bisa digunakan hanyalah format file font WOFF2, yang memang menjadi tujuan awal desain Brotli, tetapi untuk memanfaatkannya kita harus membuat seluruh file font
      Browser modern biasanya membersihkan font dengan OpenType Sanitizer (OTS), karena memasukkan file font tidak tepercaya langsung ke sistem sangat berbahaya. Jadi kita harus membuat file WOFF2 yang cukup normal agar diterima OTS, tetapi tetap memuat rangkaian byte yang diinginkan di dalamnya dan bisa diekstrak
      Setelah banyak kegagalan, gagasan untuk akhirnya memakai lebar glyph, yaitu advance, yang dienkode sebagai deret integer bertanda 2 byte nyaris tanpa batasan itu benar-benar fantastis
    • Koreksi: ini masih berjalan di Firefox. Saya hanya lupa bahwa di Firefox rasio zoom harus tepat 100%
    • Teknik ini benar-benar menakjubkan dan jauh lebih keren daripada tulisan saya. Salut
  • Pihak Chromium sudah lama memblokirnya, tetapi zstd sekarang juga mulai masuk ke web. Karena akhirnya sudah masuk Chrome, tinggal Safari saja yang perlu menyusul

    • Saya ingin semuanya beralih ke Zstandard, tetapi untuk kasus khusus ini, sejauh yang saya tahu Brotli dan Zstandard hampir setara ketika penggunaan memori dekompresornya sama
    • Setidaknya tampaknya sudah masuk daftar pekerjaan: https://webkit.org/standards-positions/#position-168
  • Saya sedang membuat Batch Compress(https://batchcompress.com/en), dan belum lama setelah menambahkan dukungan WebP, saya mengubahnya menjadi default
    Sepengetahuan saya, alat ini sudah menghasilkan JPEG terkecil di antara alat kompresi web, tetapi WebP hanya berukuran sekitar 50% dari JPEG. Setelah dukungannya ditambahkan, menjadikannya default adalah keputusan yang mudah
    Karena situs ini punya cukup banyak pengguna, saya sempat mengira akan ada beberapa keluhan setelah WebP dijadikan default, tetapi sekarang, sekitar sebulan kemudian, hanya ada satu pertanyaan atau keluhan terkait WebP
    Sepertinya sekarang hampir semua alat dan browser sudah mendukung WebP. Baru-baru ini saya hanya melihat satu situs web yang langkah berikutnya terhambat karena tidak bisa menangani unggahan gambar WebP dengan benar; selebihnya sekarang hampir semuanya mendukungnya dengan baik

    • Jika WebP mengurangi ukuran file 15–20% atau lebih dibanding JPEG, penghematan itu berasal dari penurunan kualitas, bukan peningkatan kompresi
      Jika JPEG dikompresi dan dioptimalkan dengan baik, seharusnya tidak tertinggal jauh dari WebP
      Anda memang selalu bisa membuat WebP yang tampak hampir sama dengan JPEG lalu mengurangi ukuran file, tetapi hal yang sama juga berlaku jika mengompres ulangnya menjadi JPEG yang tampak hampir sama
      Ini adalah sifat semua codec kompresi lossy, dan karena ukuran file meningkat secara eksponensial seiring naiknya kualitas, orang selalu terkejut bahwa penurunan kualitas yang sangat kecil dan nyaris tak terlihat saja bisa membuat ukuran file berbeda jauh
    • Saya penasaran klaim bahwa WebP berukuran sekitar 50% dari JPEG itu memakai metrik perbandingan kualitas yang mana
      WebP dulu terkenal buruk karena default-nya yang mengacaukan detail di area gelap
  • Saat melihat-lihat sumbernya, saya melihat ada spasi yang hilang pada deklarasi doctype. Bentuk saat ini salah, dan seharusnya ada spasi

  • Saya pernah mencoba trik ini dulu. Anehnya saya tidak ingat dipakai untuk apa, tapi mungkin saat itu hanya ingin melihat apakah bisa, dan saya juga meninggalkan komentar di sini: https://gist.github.com/gasman/2560551?permalink_comment_id=...
    Saya juga menemukan prototipe lama, sepertinya hanya tes: https://retr0.id/stuff/bee_movie.webp.html

    • Halaman itu merusak ekstensi gesture mouse saya
      Sekarang hal seperti injeksi skrip disebut ekstensi, bukan add-on, tetapi bagaimanapun pendekatan awalnya menarik: mengirim “sampah”, lalu menempelkan JS di belakangnya dan mengembalikannya ke halaman
      Sisi penggila keamanan dalam diri saya jadi bertanya-tanya apakah serangan bisa dimungkinkan saat ada data yang diberikan pengguna, seperti form komentar
      Mungkin saja seseorang menemukan urutan byte untuk dimasukkan ke komentar, lalu setelah dikompresi berubah menjadi tag script yang berada sebelum skrip saya dan dieksekusi
    • Menurut pengalaman saya, WebP tidak terlalu cocok untuk kasus umum yang membuat teknik ini benar-benar berguna, yaitu data di bawah 10 KB
      Sebagian besar hal yang ditambahkan WebP lossless di atas PNG berkaitan dengan pemodelan, bukan encoding, sedangkan kompresi teks seperti ini hanya memakai bagian encoding dari WebP
  • Menghapus Google Fonts juga akan sedikit memperbaiki waktu muat halaman, karena dimuat dari server jarak jauh dan membutuhkan handshake tambahan

    • Namun jika cukup banyak situs lain juga memakai font itu, mungkin saja font tersebut sudah ada secara lokal
  • Halaman ini rusak setidaknya di browser Sailfish OS. Ada ruang kosong panjang setelah paragraf berikut
    “Alright, so we’re dealing with 92 KiB for gzip vs 37 + 71 KiB for Brotli. Umm…”
    Meski begitu, overhead kompresi HTML gzip dan Brotli tidak ada apa-apanya dibanding jumlah JS, gambar, dan video yang dipakai situs web masa kini

    • Sama juga di Orion, Safari, dan LibreWolf. Apakah ini halaman khusus Chrome?
    • Sama juga di Mull
  • Secara pribadi saya kurang suka format ini. Saat menyimpan gambar dan tersimpan sebagai WebP, hampir tidak ada yang mendukungnya selain browser web, jadi harus dikonversi dulu sebelum bisa diedit atau dipakai secara bermakna
    Rasanya seperti dipaksa melakukan satu langkah tambahan saja

    • Ironisnya, bahkan Slides, produk Google, tidak mendukung gambar WebP
      Namun kalau dukungannya makin luas, sepertinya tidak apa-apa. Format baru sekali tiap 20 tahun masih bisa ditoleransi
      .webm boleh saja menghilang
    • Konversi hanya butuh 2 detik. Di macOS secara harfiah ada di menu klik kanan, dan ukurannya juga lebih kecil, jadi bukan masalah berarti