1 poin oleh GN⁺ 2025-02-22 | 1 komentar | Bagikan ke WhatsApp
  • Mengimplementasikan eksperimen Pong yang memanfaatkan ruang kosong di bar tab yang belum ditutup seperti layar, dengan memakai favicon dari 240 tab Chrome sebagai piksel
  • Grid tab 8×30 dibuat dengan AppleScript, dan setiap tab menghitung posisinya sendiri dari windowIndex dan tabIndex pada query URL
  • setInterval pada tab latar belakang melambat menjadi sekitar interval 1 detik, tetapi setelah timer dipindahkan ke Web Worker, pembaruan favicon menjadi cukup cepat
  • Alih-alih WebSocket server, Broadcast Channel API digunakan untuk menyinkronkan tab pada domain yang sama, dan tab main memulai animasi setelah semua tab selesai terdaftar
  • Bottleneck terbesar adalah membuat ulang ratusan favicon pada setiap frame; setelah hanya tab yang benar-benar berubah yang diperbarui, animasi menjadi jauh lebih mulus

Cara menggunakan 240 tab sebagai satu layar

  • Proyek ini adalah eksperimen menjalankan Pong dengan memanfaatkan ruang yang ditempati tab browser yang belum ditutup
  • Layarnya terdiri dari grid tab 8×30 yang dibuat dari 8 jendela Chrome dan 30 tab per jendela
  • Bola dan paddle bergerak menyambung antara HTML canvas di jendela depan dan area favicon di bar tab bagian atas
  • Kodenya dipublikasikan di GitHub sebagai faviconic, dengan catatan bahwa ini adalah kode “awful” berstatus prototipe

Ide yang bermula dari Flappy Favi

  • Inspirasinya berasal dari Flappy Favi, versi Flappy Bird buatan Tru yang berjalan di dalam favicon
  • Favicon adalah gambar kecil yang ditampilkan di bar tab browser, tetapi satu favicon saja terlalu kecil sehingga sulit dilihat
  • Untuk menggambar gambar di banyak tab, ada tiga masalah yang perlu diselesaikan terlebih dahulu
    • Cara menata tab menjadi grid yang enak dilihat
    • Cara memperbarui favicon dengan cepat bahkan di tab latar belakang
    • Cara menyinkronkan tampilan di banyak tab satu sama lain

Membuat grid tab Chrome dengan AppleScript

  • Awalnya, kemungkinan ini diuji dengan berulang kali menekan tombol tab baru di Chrome agar tab mengecil, lalu menempatkan jendela kedua secara sejajar untuk membuat baris kedua
  • Membuat grid besar secara manual merepotkan, jadi prosesnya diotomatiskan dengan AppleScript
  • Script membuka 8 jendela Chrome, membuat 30 tab di setiap jendela, dan memosisikan jendela-jendela itu agar saling bertumpuk
  • Karena ada masalah Chrome mencoba membuka kembali tab yang sudah ditutup, hal ini harus dibereskan saat script dimulai
  • Setiap URL tab memuat windowIndex dan tabIndex
    • Tab yang dibuka terakhir diberi parameter tambahan seperti isMain=true, numWindows, numTabs, dan fullWidth

Memperbarui favicon dengan cepat di tab latar belakang

  • Browser mencari URL favicon default, tetapi ikon dapat diganti dengan menambahkan elemen yang menunjukkan lokasi favicon ke HTML head lalu memperbaruinya
  • Pembaruan favicon di Chrome terasa sekitar 4 kali per detik
  • Di tab latar belakang, penggunaan resource dibatasi, sehingga loop kecil yang mengganti favicon tiap 250 ms pun hanya berjalan sekitar sekali per detik
  • Web Audio API sempat dicoba sebagai jalan pintas melalui callback audio latar belakang, tetapi tidak berhasil dibuat berjalan dengan benar
  • Setelah itu, timer dipindahkan ke Web Worker, dan masalah terselesaikan ketika worker mengirim pesan ke main document untuk memperbarui favicon
  • Struktur contohnya adalah worker menggambar emoji ke OffscreenCanvas, mengubahnya menjadi data URL, lalu mengirimkannya ke main document
    • Main document memperbarui favicon sebenarnya dengan mengubah href dari link rel="shortcut icon"

Menangani registrasi dan sinkronisasi tab dengan Broadcast Channel

  • Ada dua tantangan dalam komunikasi antartab
    • Setiap tab harus mengetahui ia tab ke berapa di jendela ke berapa
    • Harus menentukan kanal komunikasi apa yang dipakai untuk mengirim pembaruan tab
  • Penentuan posisi diselesaikan dengan membaca windowIndex dan tabIndex dari parameter query URL yang dimasukkan oleh AppleScript
  • Proof of concept awal menggunakan struktur di mana tiap tab terhubung ke server WebSocket dan server mengirim gambar
  • Pendekatan WebSocket kurang cocok dengan tujuan proyek
    • Bertentangan dengan alasan estetis untuk mendistribusikannya di tab browser di seluruh dunia tanpa server
    • Waktu koneksi tiap tab berbeda-beda, sehingga pembaruan tidak tersinkronisasi
  • Kemudian beralih ke Broadcast Channel API, yang menyampaikan informasi ke banyak tab pada domain yang sama
  • Tab latar belakang mengirim pesan registration berisi indeks tab/jendelanya melalui broadcast channel, dan tab main menerimanya lalu mengembalikan ack
  • Tab main memulai animasi setelah menerima registrasi dari semua tab latar belakang

Menyelaraskan canvas dan favicon

  • Agar objek yang digambar di jendela depan terlihat seperti “masuk” ke bar tab, canvas sebenarnya dan favicon di bagian atas diperlakukan seperti satu canvas yang diperluas
  • Implementasinya memerlukan pengukuran langsung dimensi jendela Chrome yang sebenarnya
    • 92 piksel dari sisi kiri jendela Chrome ke favicon pertama
    • 58 piksel dari bagian bawah favicon ke bagian atas jendela sebenarnya
    • Ukuran favicon adalah 16×16
  • Dengan nilai pengukuran ini serta jumlah tab/jendela, tiga nilai dihitung
    • Lebar canvas yang akan ditampilkan agar sejajar dengan favicon
    • Lebar tiap tab
    • Lebar dan tinggi keseluruhan “canvas” yang mencakup baris favicon, URL bar, dan canvas sebenarnya
  • Jika sebuah persegi berada di bawah URL bar, ia digambar di canvas sebenarnya; jika berada di atas URL bar, informasi area tersebut di-broadcast ke tab lain
  • Setiap tab menghitung koordinat pikselnya sendiri lalu memperbarui favicon menjadi piksel putih/hitam

Bottleneck-nya bukan transfer data, melainkan pembuatan favicon

  • Implementasi awal berfungsi, tetapi animasi canvas tersendat, dan tetap tidak mulus meskipun digambar di dalam requestAnimationFrame
  • Awalnya diduga main thread menghitung semua status piksel favicon lalu memasukkannya ke broadcast channel, sementara ratusan tab membacanya, sehingga terlalu banyak data yang disalin
  • Struktur diubah agar hanya posisi persegi yang di-broadcast dan tiap tab menghitung sendiri apakah terjadi perpotongan, tetapi performa tidak membaik
  • Masalah sebenarnya adalah tiap tab membuat ulang gambar favicon hitam-putih 4×4 pada setiap frame dan menghasilkan URL dengan toDataURL("image/png")
  • Walaupun canvas hasil tidak berubah, ratusan tab terus meregenerasi favicon putih kecil berkali-kali per detik
  • Setelah diperbaiki agar favicon hanya diperbarui ketika benar-benar berubah, performa meningkat drastis
  • Namun, masih belum jelas mengapa pekerjaan di tab lain bisa membuat animasi di tab depan melambat sedemikian rupa

Alasan memilih Pong, bukan Snake

  • Setelah eksperimen gerakan persegi, “engine” kecilnya dirapikan, lalu dipilih game yang akan dibuat di atasnya
  • Kandidat pertama adalah Snake, karena berbasis blok dan tampak cocok dengan favicon
  • Namun, Snake dinilai terlalu berbasis blok, sehingga efek gerakan kontinu di canvas depan yang berubah menjadi piksel diskret di bar tab kurang terasa
  • Pada akhirnya Pong dipilih
    • Bola dan paddle perlu sering bolak-balik antara canvas dan bar tab
    • Pergerakan ini cocok dengan efek visual proyek

Catatan implementasi Pong

  • Setelah API untuk menggambar favicon siap, implementasi Pong sendiri relatif sederhana
  • Pemain komputer di sebelah kanan selalu mencoba menyelaraskan pusat paddle dengan pusat bola
  • Sudut pantulan bola saat mengenai paddle dihitung dengan trigonometri sederhana menggunakan posisi relatif terhadap pusat paddle
  • Cara ini tidak realistis, tetapi mencegah bola selalu mempertahankan sudut yang sama
  • Untuk deteksi tabrakan, digunakan fungsi yang memeriksa apakah dua persegi panjang saling berpotongan
  • Untuk menonjolkan efek bola dan paddle yang masuk mulus ke favicon, ditambahkan trail pada bola

Game keempat yang dibuat di Recurse Center

  • Proyek ini dibuat selama batch di Recurse Center
  • Recurse Center diperkenalkan sebagai semacam writer’s retreat gratis untuk pemrograman
  • Proyek ini adalah game ke-4 yang dibuat dalam sekitar 40 hari
  • Pembaca yang berminat diarahkan untuk mempertimbangkan mendaftar ke Recurse Center

1 komentar

 
GN⁺ 2025-02-22
Komentar Hacker News
  • Halo! Saya yang membuat ini. Saya penasaran apakah ini akan disukai pembaca HN :)
    Kalau ada pertanyaan, saya akan jawab. Beberapa pikiran acak yang saya catat: saya sangat penasaran bagaimana tampilannya jika memakai favicon animasi. Firefox mendukung favicon animasi, jadi misalnya dengan memprediksi posisi bola di masa depan lalu membuat SVG animasi, sepertinya frame rate bisa jauh lebih baik.
    Seorang teman memberi tahu saya secara langsung bahwa rasterisasi canvas biasanya ditangani GPU, jadi tampaknya intuisi performa yang saya miliki saat melihat animasi yang patah-patah itu ternyata tidak begitu tepat.
    Saya hanya agak yakin bahwa Chrome membatasi pembaruan favicon hingga 4 kali per detik. Ada beberapa cara untuk memperbarui favicon, jadi mungkin ada sesuatu yang saya lewatkan

    • Dari sesama pembuat Pong yang absurd, ini benar-benar karya yang keren. Seni performans teknis seperti ini menyenangkan baik untuk dibuat maupun ditonton. Mungkin ini juga terhubung dengan alasan saya melakukan juggling: “Pasti ada cara untuk membuat ini lebih sulit”
    • Apakah bagian itu sudah terselesaikan? Untuk referensi desain game JavaScript sederhana, blog Jake Gordon bagus. Dia memakai game loop buffer ganda dengan canvas depan/belakang.
      [0] https://jakesgordon.com/writing/javascript-pong/part1/
    • Ini luar biasa, dan kecepatan kerjanya benar-benar hebat!
      Sejak 1 Million Checkboxes, lalu ini, global cap locks, bad apple regex, uuid, dan seterusnya, dorongan eksekusinya sangat mengesankan
    • Sekarang saya jadi ingin melihat Doom yang berjalan di tab browser
  • Saya menonton presentasi Recurse terbaru dari Nolen, dan game sekali pakai yang benar-benar gila tapi pada dasarnya lucu dan keren seperti ini sangat menyenangkan.
    Ini mengingatkan saya pada internet lama. Masa ketika orang-orang membuat sesuatu hanya untuk bersikap silly dan bersenang-senang. Tadi malam, terinspirasi oleh hal-hal yang dia unggah, saya membuat pseudo-quina yang sangat lucu, di mana halaman mencetak kode sumbernya sendiri menggunakan dekompilasi BEAM dan beberapa trik.
    Saya juga berharap punya waktu untuk terus menghasilkan hal-hal seperti ini, dan hanya mengetahui bahwa masih ada orang-orang yang terus membuat hal menarik dengan cara seperti ini saja sudah membuat saya tersenyum.
    Untuk yang ingin tertawa, bagian sort-of quine itu ada di sini: https://github.com/notactuallytreyanastasio/blog/blob/main/l...

  • Apa pun yang dibuat Nolen selalu bagus. Bagi saya, itu seperti tepat mengenai sweet spot dari aplikasi/situs satu tujuan yang memberi nostalgia tentang seperti apa internet dulu

  • Ada eksperimen serupa dari Matthew Rayfield yang menggunakan bilah alamat alih-alih favicon tab: https://www.youtube.com/watch?v=q7GtCLwTmV4

    • Karya Matt adalah inspirasi besar bagi saya!
      Saat membuat ini saya jelas memikirkan karya URL-nya, tetapi sudah cukup lama sejak terakhir kali saya benar-benar membaca blognya atau menonton videonya. Sangat lucu ketika saya menengoknya lagi di tengah proyek dan teringat bahwa dia juga menuju arah “menumpuk jendela untuk membuat 2D”
  • Ini mengingatkan saya pada:
    “Show HN: I saw this mind-blowing experiment, so I made a simple version of it” (25.11.2023) https://news.ycombinator.com/item?id=38413660
    “Synchronize a 3D scene across multiple windows using Three.js and localStorage” (27.11.2023) https://news.ycombinator.com/item?id=38437773

  • Saya berani memastikan berikutnya adalah Doom

    • Doom agak merepotkan. Setahu saya ada batas 4 frame per detik, dan memang itulah frekuensi tab bisa diperbarui.
      Selain itu, kemungkinan lebih banyak bagian layar berubah di setiap frame, jadi untuk memperbarui favicon akan perlu jauh lebih banyak proses “mengubah canvas menjadi data URL”. Dalam implementasi saat ini, ini mahal. Tapi saya belum tahu tentang OffscreenCanvas, jadi mungkin itu bisa membantu
    • Kalau selera Anda seperti itu, setahu saya Nolen sudah pernah menjalankan Doom di iPhoto
  • Jika masalahnya adalah tab di Firefox menjadi lebih sempit, Anda masih bisa memeriksa browser Firefox itu sendiri, menemukan aturan gaya lebar tab minimum, lalu memasukkannya ke userChrome.css.
    Ada beberapa panduan terkait, dan mungkin seseorang sudah merangkumnya khusus untuk lebar tab.
    Meski begitu, ini mungkin terasa curang. Ada juga browser.tabs.tabMinWidth, tetapi kalau saya ingat dengan benar, masih ada nilai minimum yang dipaksakan

  • Dulu band Ok Go pernah membuat video musik bekerja sama dengan Google Chrome, dengan jendela browser dan para penari yang tersinkronisasi dengan sangat keren serta efek seperti kaleidoskop. Ini mengingatkan saya pada itu