1 poin oleh GN⁺ 23 hari lalu | 1 komentar | Bagikan ke WhatsApp
  • Eksperimen yang merender 3D DOOM hanya dengan CSS, dengan semua dinding dan objek dibangun dari <div> serta transformasi 3D (transform)
  • Logika game ditangani oleh JavaScript, tetapi rendering sepenuhnya dilakukan oleh CSS, untuk mengeksplorasi batas browser dan CSS modern
  • Memanfaatkan fitur CSS modern seperti trigonometri, clip-path, @property, filter SVG, dan anchor positioning untuk membuat dinding, lantai, pencahayaan, sprite, hingga efek ledakan
  • Karena CSS tidak memiliki konsep kamera, sudut pandang ditangani dengan menggerakkan dunia alih-alih pemain, dan semua pergerakan dikendalikan lewat pembaruan properti kustom
  • Performanya memang belum setara WebGL, tetapi ini menunjukkan kemungkinan perluasan kemampuan ekspresi dan komputasi CSS

Rendering 3D DOOM dengan CSS

  • Proyek eksperimen yang merender DOOM hanya dengan CSS, dengan semua dinding, lantai, dan objek disusun dari <div> dan ditempatkan menggunakan transformasi 3D (transform)
    • Logika game berjalan di JavaScript, tetapi rendering sepenuhnya ditangani oleh CSS
    • Tujuan proyek ini adalah mengeksplorasi batas browser dan CSS modern

Kembali ke matematika SMA

  • Mengekstrak data file WAD DOOM asli (vertices, linedefs, sidedefs, sectors) untuk membangun adegan statis dari ribuan <div>
  • Setiap dinding menerima koordinat awal-akhir serta tinggi lantai-langit-langit melalui properti kustom CSS
  • Fungsi CSS hypot() dan atan2() digunakan untuk menghitung panjang dinding dan sudut rotasinya
  • JavaScript mengirim data mentah, lalu CSS menghitung trigonometri untuk melakukan rendering
  • Game loop dan renderer dipisahkan, sehingga JS hanya menangani manajemen status dan pembaruan koordinat

Masalah transformasi sistem koordinat

  • DOOM memakai sistem koordinat 2D dengan Y bertambah ke utara, sementara CSS 3D memakai Y ke atas dan Z mengarah ke penonton
  • Saat transformasi, digunakan bentuk translate3d(x,-z,-y) untuk menyelaraskan sistem koordinat
  • Yang menarik, perhitungan rotateY(atan2(var(--delta-y), var(--delta-x))) berjalan tanpa transformasi tambahan

Menggerakkan dunia alih-alih kamera

  • CSS tidak memiliki konsep kamera, jadi dipakai pendekatan menggerakkan dunia secara terbalik terhadap pemain
  • Hanya empat properti kustom --player-x/y/z/angle yang diperbarui dari JS
  • translate: 0 0 var(--perspective) dipakai untuk koreksi sudut pandang, sementara rotateY dan translate3d menangani rotasi pandangan dan perpindahan posisi
  • Semua pergerakan ditangani hanya lewat pembaruan properti

Lantai adalah div yang direbahkan

  • Karena elemen DOM dasar berupa bidang vertikal, lantai ditempatkan horizontal dengan rotateX(90deg)
  • clip-path, polygon(), dan path() digunakan untuk merepresentasikan area poligon kompleks dan lubang
  • Fungsi CSS modern shape() memungkinkan penggunaan jalur berbasis persentase bersama aturan evenodd

Penyelarasan tekstur

  • Agar tekstur antarsektor yang bersebelahan tidak terputus, digunakan background-position berbasis koordinat dunia
  • Semua sektor berbagi grid tekstur yang sama untuk menghasilkan sambungan batas yang mulus

Pintu, lift, dan animasi @property

  • Pembukaan pintu diwujudkan dengan menaikkan langit-langit sektor, dan transform pada kontainer <div> ditangani lewat transition CSS
  • Lift menggerakkan pemain bersama platform, sehingga JS menyinkronkan --player-z
  • Dengan @property, properti kustom didaftarkan sebagai nilai numerik agar efek jatuh dan perpindahan bisa halus

Sprite dan mirroring

  • Sprite musuh selalu menghadap kamera dengan pendekatan billboard
  • Dari 8 arah, hanya 5 set yang memakai gambar asli, sisanya ditangani dengan pembalikan kiri-kanan (scaleX)
  • Pergantian frame berjalan, menyerang, dan mati menggunakan animasi steps()
  • Masalah semua musuh berjalan serempak diatasi dengan animation-delay acak dari JS

Proyektil, ledakan, dan efek peluru

  • Roket, bola api, dan sejenisnya dipindahkan otomatis dari A→B lewat animasi CSS
  • JS hanya menetapkan koordinat awal-akhir dan durasi, lalu saat tabrakan elemen dihapus dan sprite ledakan dibuat
  • Ledakan dan asap peluru dihapus otomatis setelah animasi 3 frame berbasis steps()

Pencahayaan dan filter

  • Nilai kecerahan per sektor ditetapkan lewat properti --light, lalu elemen di dalamnya mewarisi filter: brightness()
  • Lampu berkedip dibuat dengan @keyframes yang mengubah nilai --light secara berkala
  • Musuh transparan (Spectre) direpresentasikan sebagai siluet terdistorsi dengan filter SVG (feColorMatrix, feTurbulence, feDisplacementMap)

UI responsif dan anchor positioning

  • Game ini responsif untuk mobile, dan HUD membungkus baris dengan flex-wrap
  • Sprite senjata menyesuaikan posisi otomatis terhadap tinggi HUD menggunakan anchor-name / position-anchor
  • Tombol kontrol sentuh juga ditempatkan dengan pendekatan anchor yang sama

Mode penonton

  • Mendukung tampilan seluruh peta dan sudut pandang kejar orang ketiga
  • Fungsi CSS sin() dan cos() digunakan untuk menghitung posisi kamera di belakang pemain
  • Properti rotate dan translate dipisahkan untuk menghasilkan transisi sudut pandang yang halus
  • JS hanya memperbarui posisi dan sudut, sedangkan matematika kamera ditangani oleh CSS

Culling dan performa

  • Ribuan elemen 3D menimbulkan beban pada compositor browser
  • Culling berbasis JS: elemen di luar pandangan diatur menjadi hidden
  • Eksperimen culling berbasis CSS: mengontrol visibility lewat nilai terhitung, dengan trik type grinding
  • Jika fungsi if() distandardisasi, ini bisa diganti dengan ekspresi kondisi yang lebih ringkas

Pengurutan kedalaman

  • Browser menangani pengurutan kedalaman (z-order) secara otomatis
  • Objek pada bidang yang sama diberi offset kecil untuk mencegah flicker

“Tipuan” DOOM dan penanganan langit

  • DOOM asli memakai trik proyeksi dengan menggambar langit sebagai tekstur 2D “di atas” dinding
  • Karena renderer CSS harus menempatkan langit di ruang 3D nyata, pada beberapa adegan muncul masalah bagian belakang peta terlihat
  • Solusinya adalah mengecualikan elemen di belakang dinding langit dari rendering pada tahap culling

Kesimpulan — batas dan kemungkinan CSS

  • Seluruh game loop dipisahkan: JS untuk logika, sementara rendering berbasis CSS murni
  • Fitur CSS modern seperti trigonometri, @property, clip-path, filter SVG, dan anchor positioning didorong sampai batas maksimal
  • Walau belum setara performa WebGL, proyek ini membuktikan potensi perluasan daya ekspresi CSS
  • Ditemukan banyak bug 3D dan isu performa di Safari dan Chrome
  • Kesimpulan akhirnya: “Bisakah DOOM dijalankan dengan CSS?” → Bisa. Yes, it can.

1 komentar

 
GN⁺ 23 hari lalu
Komentar Hacker News
  • Menurut saya, orang-orang tipe “saya menjalankan ini dengan DOOM” seharusnya direkrut oleh divisi sistem propulsi luar angkasa pemerintah
    Mereka adalah tipe orang yang butuh tantangan yang sangat tidak biasa, bukan sekadar sibuk menggerakkan jari

    • Tapi pada akhirnya, sepertinya bahkan sistem propulsi yang mereka buat pun akan dibuat bisa menjalankan DOOM
  • Ini terasa seperti proyek tipe “karena bisa, jadi dilakukan”
    CSS awalnya adalah bahasa styling deklaratif, tetapi sekarang terus berubah menjadi sistem yang dapat diprogram dengan hadirnya kondisi, fungsi matematika, dan trik rendering
    Yang penting bukan “apakah DOOM bisa dijalankan dengan CSS”, melainkan seberapa banyak logika yang kita dorong ke layer yang sebenarnya tidak dirancang untuk itu

    • Ini adalah contoh klasik abstraction inversion
      CSS menyembunyikan hasratnya untuk menjadi bahasa pemrograman, tetapi pada akhirnya malah berubah menjadi abstraksi yang sepenuhnya keliru
    • Intinya adalah sampai sejauh mana batas antara presentasi (CSS) dan interaksi (JavaScript)
      Dulu kita perlu JS untuk dropdown, tooltip, dan layout, tetapi sekarang properti CSS sudah bisa menentukan anchor positioning bahkan kondisi if()
      Animasi, toggle detail, sampai efek terkait aksesibilitas sekarang juga bisa ditangani dengan CSS
  • Membuat adegan 3D dengan CSS sebenarnya sudah lama memungkinkan, tetapi interaksi tetap memerlukan JS
    Sekarang, seperti pada proyek x86CSS, bahkan CPU bisa diemulasikan hanya dengan CSS tanpa JS
    Karena itu, menarik untuk bertanya apakah DOOM juga bisa diwujudkan secara real-time dengan CSS murni

    • Tetapi CPU x86 berbasis CSS terlalu lambat untuk menangani game loop. Pada akhirnya JS tetap diperlukan
    • Evolusi CSS seperti ini adalah hasil yang sudah bisa diprediksi, dan menurut saya kubu HTML seharusnya sejak awal mengadopsi DSSSL
  • Kasus ini menunjukkan dengan sangat jelas mengapa orang menginginkan CSS berbasis TypeScript
    Karena fitur seperti if() yang hanya bekerja di Chrome, para developer akhirnya memakai trik-trik seperti ini
    Misalnya, menggunakan animation-delay dan @keyframes untuk meniru toggle visibilitas
    Jika CSS if() distandardisasi, kondisi bisa ditangani dengan rapi tanpa perlu hack seperti itu

  • Cheat code DOOM IDDQD dan IDKFA sayangnya tidak berfungsi

  • Ini mengingatkan pada masa ketika membuat sudut membulat pada div memerlukan empat GIF

    • Div? Bahkan ada masa sebelumnya ketika semuanya dibuat dengan layout table
  • Benar-benar mengesankan! Cukup hapus satu div saja dan kita bisa melakukan wall hack

    • Bahkan lebih jauh lagi, cukup beri .wall nilai opacity: 0.7 dan nuansa transparent wall hack gaya lama bisa direproduksi persis
  • Saya sempat berpikir, “di mana saya bisa mencobanya langsung?” dan ternyata bisa di cssdoom.wtf

    • Begitu dijalankan di ponsel, perangkat langsung terasa panas
    • Ini pertama kalinya saya menjalankan DOOM sehalus ini di perangkat mobile
    • Di Safari juga berjalan sempurna — hal seperti ini nyaris tidak pernah terjadi
    • Di Firefox berjalan cukup baik, tetapi mapping tombol Alt membuka dan menutup menu sehingga agak mengganggu
      Di Chromium justru terasa lebih tersendat, dan saya tidak menemukan tombol strafing
      Meski begitu, secara keseluruhan ini implementasi yang luar biasa
  • CSS adalah spesifikasi representatif yang menunjukkan batas desain oleh komite
    Bersama SVG, ia bersaing untuk gelar “spesifikasi paling jelek dilihat”

    • Ada juga tanggapan yang mempertanyakan apakah komentar itu ditulis hanya setelah membaca judulnya saja
  • Satu tambahan tentang implementasi keren ini:
    sebenarnya bukan pemain yang bergerak, melainkan dunianya yang bergerak
    Kamera hanyalah alat konseptual untuk menghitung sudut pandang (frustum)