1 पॉइंट द्वारा GN⁺ 2026-03-29 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • सिर्फ CSS से 3D DOOM रेंडर करने का एक प्रयोग, जिसमें सभी दीवारें और ऑब्जेक्ट <div> और 3D transform से बनाए गए हैं
  • गेम लॉजिक JavaScript संभालता है, लेकिन रेंडरिंग पूरी तरह CSS करती है, और यह ब्राउज़र व आधुनिक CSS की सीमाओं को परखता है
  • त्रिकोणमितीय फ़ंक्शन, clip-path, @property, SVG फ़िल्टर, anchor positioning जैसे आधुनिक CSS फीचर्स का उपयोग करके दीवारें, फ़्लोर, लाइटिंग, स्प्राइट और विस्फोट प्रभाव तक बनाए गए हैं
  • CSS में कैमरा की अवधारणा न होने के कारण, प्लेयर की जगह दुनिया को हिलाने के तरीके से व्यूपॉइंट संभाला जाता है, और सारी मूवमेंट custom property अपडेट से नियंत्रित होती है
  • WebGL जैसी परफ़ॉर्मेंस नहीं है, लेकिन यह CSS की अभिव्यक्ति और गणना क्षमता के विस्तार की संभावना दिखाने वाला उदाहरण है

CSS से बनाया गया 3D DOOM रेंडरिंग

  • सिर्फ CSS से DOOM रेंडर करने वाला एक प्रयोगात्मक प्रोजेक्ट, जिसमें सभी दीवारें, फ़्लोर और ऑब्जेक्ट <div> से बने हैं और 3D transform से प्लेस किए गए हैं
    • गेम लॉजिक JavaScript में चलता है, लेकिन रेंडरिंग पूरी तरह CSS संभालती है
    • प्रोजेक्ट का उद्देश्य ब्राउज़र और आधुनिक CSS की सीमाओं की पड़ताल करना है

हाई स्कूल गणित की ओर वापसी

  • मूल DOOM की WAD फ़ाइल डेटा (vertices, linedefs, sidedefs, sectors) निकालकर हज़ारों <div> के साथ एक स्थिर दृश्य बनाया गया
  • हर दीवार के शुरुआती और अंतिम निर्देशांक, और फ़्लोर व छत की ऊँचाई CSS custom properties के रूप में दी जाती है
  • hypot() और atan2() CSS फ़ंक्शनों से दीवार की लंबाई और rotation angle की गणना की जाती है
  • JavaScript कच्चा डेटा देता है, और CSS त्रिकोणमितीय गणनाएँ करके रेंडरिंग करती है
  • गेम लूप और renderer अलग हैं, इसलिए JS सिर्फ state management और coordinate updates संभालता है

coordinate system रूपांतरण की समस्या

  • DOOM ऐसा 2D coordinate system इस्तेमाल करता है जिसमें Y उत्तर की ओर बढ़ता है, जबकि CSS 3D में Y ऊपर की ओर और Z दर्शक की दिशा में होता है
  • रूपांतरण के समय translate3d(x,-z,-y) फ़ॉर्म का उपयोग करके coordinate system मिलाया जाता है
  • rotateY(atan2(var(--delta-y), var(--delta-x))) गणना का बिना अतिरिक्त रूपांतरण के काम करना इसकी खास बात है

कैमरा की जगह दुनिया को हिलाना

  • CSS में कैमरा की अवधारणा नहीं है, इसलिए प्लेयर की जगह दुनिया को उल्टी दिशा में हिलाने का तरीका अपनाया गया है
  • JS से सिर्फ --player-x/y/z/angle ये चार custom properties अपडेट होती हैं
  • translate: 0 0 var(--perspective) से व्यूपॉइंट समायोजन, और rotateYtranslate3d से दृश्य rotation और position movement किया जाता है
  • सारी मूवमेंट सिर्फ property updates से संभाली जाती है

फ़्लोर एक लेटा हुआ div है

  • क्योंकि सामान्य DOM element एक vertical plane होता है, इसलिए फ़्लोर को rotateX(90deg) से लिटाकर horizontal रखा जाता है
  • clip-path, polygon(), path() से जटिल बहुभुज क्षेत्र और छेद दिखाए जाते हैं
  • आधुनिक CSS का shape() फ़ंक्शन percentage-based paths और evenodd नियम को साथ में उपयोग करने देता है

texture alignment

  • पास-पास के sectors के बीच texture टूटे बिना दिखे, इसके लिए world coordinates आधारित background-position का उपयोग किया गया है
  • सभी sectors एक ही texture grid साझा करते हैं, जिससे सीमाएँ स्मूद तरीके से जुड़ी रहती हैं

दरवाज़े, lift, और @property animation

  • दरवाज़ा खुलना sector की छत को ऊपर उठाने की क्रिया है, जिसे container <div> के transform पर CSS transition से किया गया है
  • lift में प्लेयर भी साथ चलता है, इसलिए JS में --player-z को sync किया जाता है
  • @property से custom properties को numeric type के रूप में register करके स्मूद गिरने और movement effects बनाए गए हैं

स्प्राइट और mirroring

  • दुश्मन sprites हमेशा कैमरा की ओर रहने वाले billboard तरीके से दिखाए जाते हैं
  • 8 दिशाओं में से सिर्फ 5 सेट असली इमेज हैं, बाकी को left-right flip (scaleX) से बनाया जाता है
  • steps() animation से चलने, हमला करने और मरने वाले frames बदले जाते हैं
  • सभी दुश्मनों के एक साथ चलने की समस्या JS के random animation-delay से हल की गई

projectile, explosion, और bullet effects

  • रॉकेट और fireball जैसी चीज़ें CSS animation से A→B movement अपने-आप करती हैं
  • JS सिर्फ शुरुआत और अंत के coordinates व duration सेट करता है, और collision होने पर element हटाकर explosion sprite बनाता है
  • explosion और bullet smoke steps() आधारित 3-frame animation के बाद अपने-आप हट जाते हैं

लाइटिंग और फ़िल्टर

  • sector के हिसाब से brightness value --light property में दी जाती है, और अंदर के elements इसे filter: brightness() से inherit करते हैं
  • झिलमिलाती लाइटें @keyframes से --light value को समय-समय पर बदलती हैं
  • पारदर्शी दुश्मन (Spectre) को SVG filters (feColorMatrix, feTurbulence, feDisplacementMap) से विकृत silhouette की तरह दिखाया गया है

responsive UI और anchor positioning

  • गेम मोबाइल-अनुकूल है, और HUD flex-wrap से लाइन बदल सकता है
  • हथियार sprites HUD की ऊँचाई के अनुसार anchor-name / position-anchor से अपने-आप adjust होते हैं
  • touch controls के बटन भी इसी anchor तरीके से लगाए गए हैं

spectator mode

  • पूरे मैप का ओवरव्यू और third-person follow view दोनों समर्थित हैं
  • CSS के sin() और cos() फ़ंक्शनों से प्लेयर के पीछे कैमरा position की गणना की जाती है
  • rotate और translate properties को अलग रखकर स्मूद viewpoint transition किया गया है
  • JS सिर्फ position और angle अपडेट करता है, और कैमरा का गणित CSS संभालती है

culling और performance

  • हज़ारों 3D elements की वजह से ब्राउज़र के compositor पर लोड पड़ता है
  • JS-आधारित culling: दृश्य से बाहर के elements को hidden किया जाता है
  • CSS-आधारित culling प्रयोग: गणना किए गए values से visibility नियंत्रित की जाती है, और type grinding ट्रिक का उपयोग होता है
  • अगर if() फ़ंक्शन standardize हो जाए, तो इसे और संक्षिप्त condition expressions से बदला जा सकता है

depth sorting

  • ब्राउज़र depth sorting (z-order) अपने-आप संभालता है
  • एक ही plane पर मौजूद objects को हल्का-सा offset देकर flicker रोका जाता है

DOOM की ‘trickery’ और sky handling

  • मूल DOOM आकाश को 2D texture के रूप में “दीवार” के ऊपर खींचने वाली projection trick इस्तेमाल करता है
  • CSS renderer को आकाश को वास्तविक 3D space में रखना पड़ता है, इसलिए कुछ दृश्यों में मैप का पिछला हिस्सा दिख जाने की समस्या आती है
  • समाधान के रूप में culling चरण में sky wall के पीछे के elements को रेंडरिंग से बाहर रखा जाता है

निष्कर्ष — CSS की सीमाएँ और संभावनाएँ

  • पूरा गेम लूप JS में है, जबकि रेंडरिंग शुद्ध CSS आधारित रूप में अलग रखी गई है
  • त्रिकोणमितीय फ़ंक्शन, @property, clip-path, SVG filters, anchor positioning जैसे आधुनिक CSS फीचर्स का भरपूर उपयोग किया गया है
  • WebGL जैसी परफ़ॉर्मेंस नहीं है, लेकिन यह CSS की अभिव्यक्तिक क्षमता के विस्तार को साबित करता है
  • Safari और Chrome में 3D से जुड़े कई bugs और performance issues भी मिले
  • अंतिम निष्कर्ष: “क्या CSS से DOOM चलाया जा सकता है?” → हाँ, चलाया जा सकता है। Yes, it can.

1 टिप्पणियां

 
GN⁺ 2026-03-29
Hacker News की राय
  • मुझे लगता है कि “इसे DOOM पर चला कर देखा” वाले लोगों को सरकार के space propulsion systems department में नौकरी मिलनी चाहिए
    वे ऐसे लोग हैं जिन्हें सिर्फ उंगलियाँ चलाने से कहीं ज़्यादा अनोखे challenges चाहिए

    • लेकिन आखिर में शायद वे जो propulsion system बनाएँगे, वह भी DOOM चला सकेगा
  • यह वैसा प्रोजेक्ट लगता है जैसा “कर सकते हैं, इसलिए करके देखते हैं”
    CSS मूल रूप से एक declarative styling language थी, लेकिन अब conditionals, math functions और rendering tricks जुड़ते-जुड़ते यह धीरे-धीरे एक programmable system बनती जा रही है
    असली सवाल यह नहीं है कि “क्या CSS में DOOM चल सकता है”, बल्कि यह है कि हम उस layer में कितना logic ठूँस रहे हैं जो मूल रूप से इस काम के लिए बना ही नहीं था

    • यह abstraction inversion का एक क्लासिक उदाहरण है
      CSS प्रोग्रामिंग language बनने की अपनी इच्छा छिपाती है, लेकिन आखिरकार यह पूरी तरह गलत abstraction में बदल गई है
    • मुख्य बात यह है कि presentation (CSS) और interaction (JavaScript) की सीमा आखिर कहाँ तक है
      पहले dropdowns, tooltips और layout के लिए JS चाहिए होता था, लेकिन अब CSS properties से anchor positioning और conditionals (if()) तक बताए जा सकते हैं
      animations, detail toggles, और accessibility से जुड़े effects भी अब CSS से संभाले जा सकते हैं
  • CSS से 3D scenes बनाना पहले से संभव था, लेकिन interaction के लिए JS चाहिए होता था
    अब x86CSS project की तरह JS के बिना भी CPU को सिर्फ CSS से emulate किया जा सकता है
    इसलिए यह जिज्ञासा होती है कि क्या DOOM को भी pure CSS में real-time चलाया जा सकता है

    • लेकिन CSS x86 CPU इतना धीमा है कि वह game loop संभाल नहीं सकता। आखिरकार JS की ज़रूरत पड़ती है
    • CSS का यह विकास पहले से अनुमानित नतीजा था, और मुझे लगता है कि HTML camp को शुरू से DSSSL अपनाना चाहिए था
  • यह उदाहरण अच्छी तरह दिखाता है कि लोग क्यों TypeScript-based CSS चाहते हैं
    Chrome-only if() जैसी features की वजह से developers ऐसे जुगाड़ करते हैं
    उदाहरण के लिए, animation-delay और @keyframes का इस्तेमाल करके visibility toggle जैसा व्यवहार नकली तौर पर बनाया जाता है
    अगर CSS if() standardize हो जाए, तो ऐसे hacks के बिना साफ-सुथरा conditional handling संभव होगा

  • DOOM के cheat codes IDDQD और IDKFA अफसोस की बात है कि काम नहीं किए

  • यह देखकर पुराने दिन याद आ गए, जब किसी div पर rounded corners बनाने के लिए चार GIF चाहिए होते थे

    • div की भी क्या बात करें, उससे पहले तो सब कुछ table layout से ही किया जाता था
  • वाकई बहुत प्रभावशाली! सिर्फ एक div हटा देने से wall hack संभव हो जाता है

    • और आगे बढ़ें तो .wall पर सिर्फ opacity: 0.7 देने से पुराने जमाने वाले transparent wallhack का एहसास भी वैसा ही बन जाता है
  • मैं सोच रहा था, “इसे खुद कहाँ चला कर देख सकते हैं?” — और यह cssdoom.wtf पर संभव है

    • फ़ोन पर चलाते ही डिवाइस गर्म होने लगा
    • मोबाइल पर DOOM को इतना smoothly चलते हुए मैंने पहली बार देखा
    • Safari में भी यह पूरी तरह काम करता है — ऐसा तो लगभग कभी नहीं होता
    • Firefox में यह ठीक चलता था, लेकिन Alt key mapping मेनू खोल-बंद कर देती थी, इसलिए असुविधा हुई
      Chromium में उल्टा यह और ज़्यादा अटक रहा था, और strafing keys मुझे मिल ही नहींीं
      फिर भी कुल मिलाकर यह हैरान कर देने वाला implementation है
  • CSS एक ऐसी representative specification है जो committee-designed systems की सीमाएँ दिखाती है
    SVG के साथ मिलकर यह “सबसे बदसूरत spec” की दौड़ में है

    • इस पर यह प्रतिक्रिया भी थी कि कहीं तुमने सिर्फ शीर्षक पढ़कर ही comment तो नहीं कर दिया
  • इस शानदार implementation के बारे में एक बात और जोड़नी हो तो,
    असल में player नहीं चलता, दुनिया चलती है
    camera सिर्फ field of view (frustum) की गणना के लिए एक वैचारिक उपकरण भर है