1 पॉइंट द्वारा GN⁺ 2024-09-08 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • GitHub Pages में Brotli सपोर्ट न होने पर, HTML को lossless WebP image में encode करके और browser के image decoder व JavaScript से restore करके transfer size घटाने का एक प्रयोग
  • WASM Brotli decoder में 71 KiB~200 KB का extra cost आता है, और Compression Streams API सिर्फ gzip, deflate, deflate-raw सपोर्ट करता है, इसलिए यह Brotli decoding bypass बनना मुश्किल है
  • VP8L lossless WebP, prediction transform, 16x16 blocks के हिसाब से Huffman tree reuse, और color cache की वजह से text-जैसे data में भी gzip से छोटा result दे सकता है
  • Test HTML 439,478 bytes से gzip में 94,683 bytes और WebP में 43,182 bytes तक घटा; यह Brotli 37 KiB से बड़ा था, लेकिन gzip के मुकाबले करीब 2.2x छोटा था
  • Canvas 2D के anti-fingerprinting noise, WebGL readPixels में बदलाव, initial blank screen, और scroll restore issues की वजह से यह real-world deployment से ज्यादा browser constraints दिखाने वाला hack है

GitHub Pages पर Brotli न इस्तेमाल कर पाने की समस्या

  • Page load time घटाते समय HTML minify की तुलना में HTTP compression ज्यादा असर डालता है
  • HTTP Content-Encoding header के जरिए gzip और Brotli सपोर्ट करता है
    • gzip सस्ता है, इसलिए अक्सर default रूप से enabled रहता है
    • Brotli आम तौर पर gzip से बेहतर compression ratio देता है, लेकिन काफी धीमा है
  • GitHub Pages Brotli सपोर्ट नहीं करता, इसलिए site का सबसे लंबा article Recovering garbled Bitcoin addresses, जो Brotli में 37 KiB हो सकता था, gzip में 92 KiB बन जाता है
  • इस अंतर की वजह से load time बेवजह 2.5x बढ़ जाता है

पहले सोचे गए विकल्प और जहां बात अटकी

  • अगर GitHub pre-compressed Brotli files upload और serve करना सपोर्ट करे, तो समस्या से बचा जा सकता है, लेकिन ऐसा feature उपलब्ध नहीं है
  • Client-side JavaScript से सीधे decompress करने का तरीका WASM decoder size की वजह से अपना फायदा खो देता है
    • brotli-dec-wasm करीब 200 KB है
    • tiny-brotli-dec-wasm 71 KiB है
    • तुलना gzip 92 KiB बनाम Brotli body 37 KiB + 71 KiB की हो जाती है, जिससे फायदा खत्म हो जाता है
  • Browser HTTP stack में Brotli decoder है, लेकिन Compression Streams API का DecompressionStream सिर्फ gzip, deflate, deflate-raw स्वीकार करता है
  • gzip को Zopfli से pre-compress करने पर भी यह 86 KiB रहता है, यानी Brotli से बड़ा

Image format को compression container की तरह इस्तेमाल करना

  • Browser images decode कर सकता है, इसलिए अगर data को image pixels में रखा जाए और Canvas API से फिर पढ़ा जाए, तो नया decompression logic जोड़ने की जरूरत नहीं पड़ती
  • GIF data को row-major order में फैलाकर LZW apply करता है, लेकिन gzip का DEFLATE LZW को replace करने के लिए design किया गया था, इसलिए फायदा मिलना मुश्किल है
  • PNG DEFLATE इस्तेमाल करता है, लेकिन pixel source को सीधे compress करने के बजाय पहले neighboring pixels के difference पर prediction transform apply करता है
    • उदाहरण: [a, b, c, d] के बजाय [a, b-a, c-b, d-c] compress करना
    • Predicted value और actual value का difference जितना छोटा होगा, Huffman compression के लिए उतना बेहतर होगा
  • इस experiment का core idea lossless WebP यानी VP8L को सामान्य byte data compression के लिए इस्तेमाल करना है

VP8L gzip से कैसे अलग है

  • WebP में lossy और lossless variants हैं; यहां सिर्फ lossless format VP8L पर बात है
  • VP8L PNG की तरह prediction transform इस्तेमाल करता है, लेकिन DEFLATE के बजाय Google का बनाया DEFLATE-जैसा तरीका इस्तेमाल करता है
  • DEFLATE file को कई chunks में बांट सकता है और हर chunk के लिए अलग Huffman tree रख सकता है
    • अगर JavaScript, SVG, और markup एक HTML में मिले हों, तो अलग-अलग trees बेहतर हो सकते हैं
  • VP8L मनचाहे आकार की Huffman tree table define करता है और हर 16x16 pixel block के लिए अलग tree इस्तेमाल कर सकता है
    • अगर JavaScript के बाद CSS और फिर JavaScript आए, तो DEFLATE समान tree को कई बार encode कर सकता है
    • VP8L tree reuse कर सकता है, इसलिए वह trees को ज्यादा बार और कम cost में बदल सकता है
  • VP8L का color cache कुछ properties वाले recent pixels को copy करने के निर्देश की तरह values को छोटा express कर सकता है

पहला WebP compression experiment

  • Test file Recovering garbled Bitcoin addresses HTML है
    • Original size: 439,478 bytes
    • gzip --best: 94,683 bytes
  • Rust के webp crate से bytes को grayscale RGB image में बदलकर lossless WebP में compress किया गया
  • Grayscale इस्तेमाल करने की वजह WebP का subtract green transform था
    • Grayscale में G channel को R/B से घटाने पर R/B प्रभावी रूप से 0 हो जाते हैं
    • WebP तीनों channels को अलग Huffman trees से encode करता है, इसलिए fixed-value channels करीब O(1) space लेते हैं
  • शुरुआत में इसे 1xN image बनाने की कोशिश की गई, लेकिन WebP maximum 16383x16383 तक ही support करता है, जिससे VP8_ENC_ERROR_BAD_DIMENSION error आया
  • 16383xN shape में fit करने पर result 45,604 bytes हुआ, जो gzip से 2x छोटा और bzip2 49,764 bytes से भी छोटा था

WebP-specific tuning

  • Wide image में row-major order इस्तेमाल करने पर input में दूर-दूर मौजूद bytes एक ही 16x16 block में मिल जाते हैं
  • Image shape को vertical long 27x16383 में बदलने पर compression result 43,232 bytes रह गया
  • cwebp की compression effort setting method को 0~6 तक compare किया गया
    • 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 को method 6 जैसी size देते हुए तेज option माना गया
  • इस state में WebP gzip से 2.2x छोटा और Brotli से 1.2x बड़ा है

कई files का benchmark

  • Compare करने के लिए snappy testdata, Canterbury Corpus और Large Corpus, और 2 SVG files ली गईं
  • Comparison formats थे gzip --best, brotli --best, bzip2 --best, और WebP compression script
  • WebP बहुत छोटी files grammar.lsp, xargs.1 और कुछ exceptions को छोड़कर लगभग हमेशा gzip से बेहतर रहा
  • Exceptions kennedy.xls और paper-100k.pdf थीं
    • paper-100k.pdf में 19 KB XML के बाद compressed data है, इसलिए यह असल में small data measure करने जैसी situation है
    • kennedy.xls में Brotli/bzip2 की relative performance भी अजीब है, और यह पास-पास अलग-अलग तरह के data वाली file हो सकती है जिसे compressor के लिए handle करना मुश्किल है
  • WebP bzip2 से थोड़ा खराब है, लेकिन कुछ cases में उससे आगे भी निकलता है
  • fireworks.jpeg जैसे लगभग uniform random blob-जैसे exception को छोड़कर WebP हमेशा Brotli से खराब रहा
  • बड़े plain-text data में इसने gzip के मुकाबले measurable improvement दिया
    • SVG files में भी improvement था
    • html_x_4 में WebP ने 3.3% compression ratio record किया, जो Brotli 2.8% से खराब, लेकिन gzip 13% से काफी बेहतर था

JavaScript से restore करना

  • WebP decoding खुद fetch, createImageBitmap, OffscreenCanvas, getImageData, TextDecoder से implement की जा सकती है
  • Structure यह है कि pixel का R channel original HTML bytes की तरह इस्तेमाल होता है, उसे UTF-8 में decode करके document.documentElement.innerHTML में डाला जाता है
  • Canvas API fingerprinting में अक्सर इस्तेमाल होती है, इसलिए कुछ browsers getImageData result में noise जोड़ते हैं
    • Firefox के strict tracking protection में 1% से कम pixels प्रभावित हो सकते हैं
    • HTML में यह noise typos की तरह दिखता है
  • WebGL का readPixels इस्तेमाल करने पर उस समय यह noise के बिना काम करता था
    • WebGL textures को reliably सिर्फ 2048x2048 तक support करता है, इसलिए size limit फिर से fit करनी पड़ती है
    • यह restore code minify के बाद करीब 550 bytes था
  • WebP और code मिलाकर 44 KiB हुए, जिनकी तुलना gzip 92 KiB और Brotli 37 KiB से है
  • 15 अप्रैल 2026 के बाद Firefox ने readPixels में भी anti-fingerprinting जोड़ दिया, इसलिए यह तरीका जैसे-का-तैसा अब काम नहीं करता

Screen flicker और scroll की समस्या

  • await promise-based तरीके से handle होता है, इसलिए WebP download खत्म होने से पहले browser script execution खत्म मान लेता है
  • DOM अभी खाली होता है, इसलिए user थोड़ी देर के लिए blank white screen देखता है
  • Mitigation के तौर पर style और page top के करीब 8 KiB gzip HTML में छोड़े जा सकते हैं, और viewport के नीचे के content को ही WebP में compress किया जा सकता है
  • Refresh पर scroll position restore भी समस्या बनता है
    • उदाहरण के लिए, अगर Y = 5000px position पर refresh किया और page height 0px है, तो position reset हो जाती है
    • बहुत बड़ा temporary div जोड़ना मदद कर सकता है
  • document.write की जगह document.documentElement.innerHTML को assign करना चाहिए, ताकि current document को नए document से replace किए बिना update किया जा सके

WebP को सीधे JavaScript में डालना

  • Latency थोड़ी और कम करने के लिए WebP को JavaScript के अंदर सीधे embed किया जा सकता है
  • सबसे simple तरीका base64 data URL है
  • base64 original size को 1.33x बढ़ाता है, लेकिन gzip इस increase को लगभग cancel कर देता है
    • compressed.webp को base64 बनाने पर 57,576 bytes
    • इसे gzip --best से compress करने पर 43,519 bytes
  • WebP जैसे compressed blobs लगभग uniform random data होते हैं, और base64 के 8-bit-to-6-bit transform के result पर gzip का Huffman tree व्यवहार में reverse transform जैसा काम करता है
  • Unicode और UTF-16 भी इस्तेमाल किए जा सकते हैं, लेकिन base64 पर्याप्त first solution बना रहता है

वास्तविक use और बाद की स्थिति

  • Article लिखे जाने के समय यह page खुद पुराने browsers या JavaScript-disabled environments को छोड़कर “Fool me twice” section से आगे WebP में compress था
  • Page की WebP image actual code में vertical लंबी और narrow है, लेकिन देखने में अच्छी लगे इसलिए square WebP example भी दिया गया था
  • Image में bright top और bottom text और code थे, करीब 1/5 position पर hatched area diagram था, और ज्यादातर dark area diagram के अंदर का text था
  • Actual saving सीमित थी
    • Existing gzip page: 88 KiB
    • WebP apply करने के बाद gzip page: 83 KiB
    • Expected Brotli: 69 KiB
  • 15 अप्रैल 2026 के बाद Firefox visitors को broken content न दिखे, इसलिए अब इसे uncompressed page में बदल दिया गया है
  • Rust code, corpus, और अन्य files GitHub पर public हैं

1 टिप्पणियां

 
GN⁺ 2024-09-08
Hacker News की राय
  • latency को अनदेखा करें तो ऐसा हो सकता है, लेकिन असल में load time करीब 0.001% बढ़ता दिखता है
    size में बढ़ोतरी round-trip latency के मुकाबले मायने नहीं रखती, और 55KiB कम भेजकर बचने वाले समय से ज्यादा समय decompression में लग सकता है
    प्रयोग दिलचस्प है, लेकिन इस मामले में user experience उल्टा खराब होने की संभावना ज्यादा है, और speed लगभग वही रहेगी जबकि compatibility कम हो जाएगी

    • अगर आप सिर्फ load time optimize कर रहे हैं और सबकी data speed मानकर चल रहे हैं तो बात सही है, लेकिन कई बार मैं नहीं चाहता कि कोई website या app लेखक मेरी ओर से speed/data tradeoff इतनी आसानी से तय कर दे
      समस्या यह है कि TFA लेखक जैसे लोग भी हैं जो 100KB को 50KB करने की कोशिश करते हैं, लेकिन ऐसी जगहें भी हैं जो roaming data पर सिर्फ restaurant के opening hours देखने आए मुझ पर बिना सोचे-समझे दर्जनों MB images भेज देती हैं
      resource awareness मौजूद है, लेकिन अफसोस कि बहुत असमान रूप से फैली हुई है
    • बात सिर्फ decompression time की नहीं है। पूरी चीज download होने के बाद ही decompress की जा सकती है, लेकिन browser server से stream हो रहे HTML को तुरंत decompress और render कर सकता है
      connection टूट जाए तो सब खो जाता है, और downloaded हिस्से को भी पढ़ना असंभव हो जाता है
      सामान्य connection पर फर्क मायने नहीं रखता, और जिन बहुत धीमे या unstable connections पर 50KB महत्वपूर्ण हो, वहाँ यह तरीका निश्चित रूप से ज्यादा खराब है। प्रयोग दिलचस्प है, लेकिन site पर इसे लागू न करें तो बेहतर होगा
    • cache में नहीं हो तो 850K की Symbols-2048-em%20Nerd%20Font%20Complete.woff2 file है, जो उस अंतर को लगभग ढक देती है
    • size का इतना अंतर जरूरी round trips की संख्या को प्रभावित करने लायक बड़ा है। किसी reasonable modern initial congestion window value के साथ करीब 1 round trip तो कम होनी चाहिए
      2.5x का फर्क शायद नहीं होगा, लेकिन 0.001% भी नहीं है
    • अगर बचत एक TCP receive window से भी कम है तो latency में फर्क नहीं होगा
      loss वाले network में फर्क पड़ सकता है, लेकिन पक्का नहीं कह सकता
  • समझ नहीं आता कि readPixels fingerprinting protection का target क्यों नहीं है। चूंकि page भर में लगभग न दिखने वाली typos नहीं बिखेरी जातीं, इसलिए मेरे लिए ठीक है
    gzip किए हुए HTML में style और page के ऊपर के करीब 8KiB ही रखे गए हैं, और viewport के नीचे वाला content WebP से compress किया गया है—यह पढ़कर समझ आया कि लेख किसी random sentence के बाद अचानक क्यों कट गया और आगे blank page क्यों आया
    मैं LibreWolf इस्तेमाल कर रहा हूँ, इसलिए WebGL बंद है, और WebGL मांगने वाले random web games के लिए Chromium इस्तेमाल करता हूँ। WebGL चालू करने पर लेख ठीक से चला, और सच कहूँ तो technique काफी neat है

    • अगर यह सभी modern web browsers में, यहाँ तक कि fingerprint protection on होने पर भी, काम नहीं करती और पुराने browsers के लिए fallback भी नहीं है, तो इसे neat कहना मुश्किल है
      WWW को साधारण HTML से शुरू होकर progressive enhancement के जरिए universal access देना चाहिए
  • web browser में Brotli को सीधे इस्तेमाल करना भी संभव है, लेकिन जाहिर है इसकी सीमाएँ हैं
    मुझे लगता है 2022 की JS1024 submission [1] इस concept का पहला demo था, और arbitrary compression के लिए proof-of-concept code भी है। दुर्भाग्य से यह अपने original उद्देश्य size coding के लिए ठीक नहीं बैठा
    मुख्य सीमा यह है कि यह व्यावहारिक रूप से ASCII characters तक सीमित है, और स्पष्ट कारणों से rendering stack के प्रति बहुत sensitive है। अभी Firefox में चलता नहीं दिखता

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

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

    • इस approach को समझने की कुंजी यह हिस्सा है, जहाँ यह जानना जरूरी नहीं कि असल में इसे कैसे ठूँसा गया
      सिर्फ उसी WOFF2 font file format का इस्तेमाल संभव है जिसके लिए Brotli मूल रूप से design किया गया था, लेकिन इसका फायदा उठाने के लिए पूरा font file बनाना पड़ता है
      modern browsers अविश्वसनीय font files को सीधे system में डालना बहुत खतरनाक मानते हैं, इसलिए वे आमतौर पर OpenType Sanitizer (OTS) से fonts को sanitize करते हैं; इसलिए ऐसा WOFF2 file बनाना पड़ता है जो OTS को स्वीकार्य लगे, फिर भी भीतर desired byte sequence रखे और उसे extract किया जा सके
      कई failures के बाद glyph widths, यानी advances, पर टिकना—जो लगभग बिना restrictions के 2-byte signed integer sequence के रूप में encode होते हैं—यह idea शानदार है
    • सुधार: यह अभी भी Firefox में काम करता है। मैं बस भूल गया था कि Firefox में zoom ratio ठीक 100% होना चाहिए
    • यह technique सचमुच कमाल की है और मेरे लेख से कहीं ज्यादा cool है। सलाम
  • Chromium side इसे लंबे समय से रोक रही थी, लेकिन zstd भी अब web में आ रहा है। आखिरकार यह Chrome में आ गया है, तो अब बस Safari के follow करने की देर है

    • मैं तो सब कुछ Zstandard पर ले जाना चाहूँगा, लेकिन इस खास case में, जहाँ तक मुझे पता है, decompressor memory usage समान होने पर Brotli और Zstandard लगभग बराबर हैं
    • कम-से-कम लगता है कि यह to-do list में है: https://webkit.org/standards-positions/#position-168
  • मैं Batch Compress(https://batchcompress.com/en) बना रहा हूँ, और हाल ही में WebP सपोर्ट जोड़ने के कुछ ही समय बाद उसे डिफ़ॉल्ट बना दिया
    मेरी जानकारी में हम पहले से ही वेब compression tools में सबसे छोटे JPEG बना रहे थे, लेकिन WebP JPEG के लगभग 50% size का ही निकला। सपोर्ट जोड़ने के बाद उसे जल्द ही डिफ़ॉल्ट बनाना आसान फैसला था
    साइट के users काफी हैं, इसलिए WebP को डिफ़ॉल्ट बनाने के बाद कुछ complaints आने की उम्मीद थी, लेकिन करीब एक महीना हो चुका है और WebP से जुड़ी inquiry या complaint सिर्फ एक ही आई
    अब लगभग सभी tools और browsers WebP support करते दिखते हैं। हाल में बस एक वेबसाइट देखी जो WebP image upload को ठीक से handle नहीं कर पा रही थी और अगला step अटक गया था; आजकल लगभग हर जगह support ठीक है

    • अगर WebP, JPEG की तुलना में file size 15~20% से ज़्यादा घटा रहा है, तो वह बचत compression improvement से नहीं बल्कि quality degradation से आ रही है
      JPEG को अच्छे से compress और optimize किया जाए तो उसे WebP से बहुत पीछे नहीं होना चाहिए
      आप कभी भी ऐसा WebP बना सकते हैं जो JPEG जैसा ही दिखे और file size घटा दे, लेकिन लगभग वैसा ही दिखने वाले JPEG में दोबारा compress करने पर भी यही होगा
      यह सभी lossy compression codecs की खासियत है, और जैसे-जैसे quality बढ़ती है file size exponential रूप से बढ़ता है, इसलिए लोग हमेशा हैरान होते हैं कि लगभग न दिखने वाली बहुत छोटी quality drop से भी file size में बड़ा फर्क आ जाता है
    • यह जानना चाहूँगा कि WebP का JPEG के लगभग 50% size का होना किस quality comparison metric पर आधारित है
      WebP की बदनामी उसके भयानक defaults के लिए थी, जो dark areas की details खराब कर देते थे
  • Source देखते समय doctype declaration में space गायब दिखी। मौजूदा रूप गलत है और उसमें space होना चाहिए

  • मैंने यह trick पहले कभी इस्तेमाल की है। अजीब बात है कि किस चीज़ के लिए की थी, याद नहीं, लेकिन शायद यह देखने के लिए कि संभव है या नहीं; और यहाँ भी comment छोड़ा था: https://gist.github.com/gasman/2560551?permalink_comment_id=...
    एक पुराना prototype भी मिला, लगता है बस test था: https://retr0.id/stuff/bee_movie.webp.html

    • उस page ने मेरा mouse gesture extension तोड़ दिया
      अब शायद उन्हें add-on नहीं, extension कहा जाता है—script injection जैसी चीज़ें—लेकिन वैसे भी शुरुआत में “कचरा” pass करके उसके बाद JS जोड़कर page में वापस लौटाने वाला approach दिलचस्प है
      मेरे अंदर का security nerd सोचने लगता है कि comments form जैसे user-provided data होने पर हमला संभव हो जाएगा या नहीं
      शायद कोई comment में डालने के लिए byte sequence खोज ले, और compression के बाद वह मेरे script से पहले स्थित होकर execute होने वाले script tag में बदल जाए
    • मेरे अनुभव में WebP इस technique के सच में उपयोगी आम case, यानी 10KB से कम data, के लिए ठीक नहीं बैठा
      WebP lossless, PNG में जो ज़्यादातर जोड़ता है वह encoding नहीं बल्कि modeling से जुड़ा है, और ऐसा text compression WebP के सिर्फ encoding वाले हिस्से का उपयोग करता है
  • Google Fonts हटाने से page load time थोड़ा बेहतर होगा। क्योंकि वे remote server से load होते हैं और extra handshake चाहिए होता है

    • लेकिन अगर काफी सारी दूसरी sites भी वही font इस्तेमाल कर रही हैं, तो वह पहले से local में हो सकता है
  • यह page कम से कम Sailfish OS browser में टूटता है। अगले paragraph के बाद लंबी खाली जगह है
    “Alright, so we’re dealing with 92 KiB for gzip vs 37 + 71 KiB for Brotli. Umm…”
    फिर भी gzip और Brotli HTML compression का overhead आजकल websites में इस्तेमाल होने वाले JS, images और video की मात्रा की तुलना में कुछ भी नहीं है

    • Orion, Safari, LibreWolf में भी यही है। क्या यह Chrome-only page है?
    • Mull में भी यही है
  • व्यक्तिगत रूप से मुझे यह format खास पसंद नहीं। Image save की और अगर वह WebP के रूप में save हो जाए, तो web browser के अलावा कहीं support नहीं मिलता, इसलिए edit करने या ढंग से इस्तेमाल करने से पहले convert करना पड़ता है
    बस एक extra step थोपने जैसा लगता है

    • विडंबना यह है कि Google का product Slides भी WebP images support नहीं करता
      फिर भी अगर support और बढ़े तो ठीक लगेगा। 20 साल में एक बार नया format आ जाए, इतना तो सहा जा सकता है
      .webm खत्म हो जाए तो अच्छा है
    • Conversion में 2 सेकंड लगते हैं। macOS में यह literally right-click menu में है, और size भी छोटा होता है, इसलिए खास समस्या नहीं है