- xkcd का Machine एक विशाल Rube Goldberg-शैली का marble machine गेम है, जिसमें पाठकों द्वारा बनाए गए टाइल-आधारित डिवाइस आपस में जोड़े जाते हैं, और टीम ने इस आइडिया को सिर्फ 3 हफ्तों में एक वास्तविक interactive comic के रूप में बना दिया
- पिछले user-participation प्रोजेक्ट्स के अनुभव से यह डिज़ाइन सिद्धांत निकला कि shared canvas को अच्छी तरह काम करने के लिए साझा संदर्भ और उद्देश्य चाहिए
- खिलाड़ियों की अभिव्यक्ति को बनाए रखते हुए टाइल compatibility के लिए input/output constraints काफ़ी सख्त रखे गए, और हर डिवाइस को 30 सेकंड के भीतर stable state तक पहुंचना ज़रूरी बनाया गया
- पूरी मशीन को real time में simulate करने के बजाय केवल दिखने वाले हिस्से को Rapier से चलाया गया, और approval के समय के snapshot का उपयोग करके उसे पहले से चल रहे डिवाइस जैसा दिखाया गया
- React और DOM rendering, Haskell backend, Redis, OpenAPI, TanStack Query, और moderation UI को जोड़कर submission approval और deployment flow चलाया गया
Machine की शुरुआत
- xkcd ने 5 अप्रैल को Machine जारी किया
- Machine क्लासिक गेम The Incredible Machine शैली का एक विशाल Rube Goldberg machine builder है
- पूरी मशीन छोटे-छोटे device tiles को जोड़कर बनाई गई है, जिन्हें अलग-अलग xkcd पाठकों ने बनाया
- टीम ने Machine को 3 हफ्तों में बनाया, और यह आइडिया 2005 के collaborative GIF Blue Ball Machine से शुरू हुआ
- शुरुआती brainstorming के मुख्य सवाल थे: marble कहाँ से आएँगे, सब लोग जो मशीन देखेंगे क्या वह एक जैसी होगी, मशीन का उद्देश्य क्या होगा, खिलाड़ी कैसे interact करेंगे, और लोग इसमें हिस्सा क्यों लेंगे
user-participation वाले xkcd से मिली सीख
- user-generated content पर केंद्रित पहले के xkcd interactive comics में Lorenz ऐसा था जहाँ पाठक panel text लिखकर jokes और कहानी को आगे बढ़ाते थे, और यह एक अच्छा अनुभव रहा
- 2020 का Collector’s Edition ऐसा था जिसमें खिलाड़ी xkcd archive से stickers ढूँढकर global shared canvas पर एक-एक बार चिपकाते थे, लेकिन यह उम्मीद के मुताबिक काम नहीं कर पाया
- सभी खिलाड़ी खाली map के बीच से शुरू करते थे, और जल्दी ही एक अव्यवस्थित स्क्रीन पहला impression बन जाती थी
- sticker की जगह सोच-समझकर चुनने की प्रेरणा कम थी, और अकेले किसी एक action से कहानी को आगे बढ़ाना मुश्किल था
- shared story या goal न होने से यह साफ़ नहीं था कि हर sticker पेज के बाकी हिस्सों से कैसे जुड़ता है
- collective canvas को अच्छी तरह काम करना है तो उपयोगकर्ताओं को उदाहरणों से सीखना चाहिए कि क्या बनाना अच्छा लगेगा
- रचनात्मक नतीजों को एक दिशा में ले जाने के लिए यह ज़रूरी है कि क्या बनाना है, इसे sync करने वाला साझा संदर्भ और उद्देश्य हो
constraints की डिज़ाइन: अभिव्यक्ति, compatibility, और 30 सेकंड की stable state
- एक बड़े collaborative marble-drop device बनाने का निर्णय लेने के बाद भी पूरी मशीन का आकार, simulation का तरीका, और tiles को जोड़ने का तरीका खुले सवाल बने रहे
- अगर मशीन का आकार 100x100 माना जाए, तो client पर real time में 10,000 tiles चलाना और हर tile में दर्जनों marbles संभालना जोखिम भरा लगा
-
correctness से ज़्यादा अभिव्यक्ति को प्राथमिकता
- पूरी मशीन को server पर चलाने या individual tiles को simulate करके verify करने जैसे विकल्पों पर विचार हुआ
- prototype editor में chaotic marble-collision patterns बहुत आसानी से बनने लगे, और टीम इस निष्कर्ष पर पहुँची कि अगर predictable machine की शर्त रखी गई तो खिलाड़ियों की आज़ादी कम हो जाएगी
- अंतिम डिज़ाइन ने player flexibility को प्राथमिकता दी, ताकि बहुत non-deterministic या टूटी हुई devices भी बनाई जा सकें
- इस फैसले की वजह से यह जाँचना ज़रूरी हुआ कि tiles constraints पूरी करती हैं या नहीं, और offensive content हटाने के लिए active moderation भी चाहिए थी
-
tiles के बीच compatibility के लिए input/output constraints
- शुरुआत में यह सोचा गया था कि अगला खिलाड़ी पिछली tile के output position के हिसाब से स्वतंत्र रूप से आगे बढ़े
- लेकिन अगर शुरू में रखी गई किसी tile को बाद में बदलना पड़े, तो उस पर निर्भर बड़ा हिस्सा टूट सकता था
- इसलिए एक ही tile space के भीतर कई खिलाड़ी आपस में compatible designs बना सकें, इसके लिए input/output constraints काफ़ी सख्त रखे गए
- यह तरीका Robustness principle के “be conservative in what you send, be liberal in what you accept” सिद्धांत से मेल खाता है
- Kevin का map generator एक साधारण 1-input 1-output puzzle से शुरू होकर बीच में 4-input 4-output merge तक जटिल होता है, फिर अंत में प्रति tile 2 outputs पर लौट आता है
- editor खिलाड़ी के tile बनाते समय real-time feedback देता है
- tile को औसतन जितनी गति से marbles मिलें, लगभग उतनी ही गति से उन्हें बाहर भी भेजना चाहिए
- marbles को निगल जाने वाली या बहुत बड़ी delay पैदा करने वाली devices को कम करना लक्ष्य था
- upstream input variation को दर्शाने के लिए editor में आने वाली marble rate को randomize करने वाला chaos testing इस्तेमाल किया गया
-
30 सेकंड के भीतर stable state तक पहुँचना चाहिए
- moderators को कितनी देर तक देखना पड़े, यह कम करने के लिए एक मनमाना नियम रखा गया कि device को 30 सेकंड के भीतर stable state में आना चाहिए
- आधार यह गणना थी कि अगर 10,000 tiles को 30-30 सेकंड देखा जाए, तो कुल moderation समय लगभग 83.3 घंटे बनता है
- marbles को भी 30 सेकंड बाद expire होने के लिए बदल दिया गया
- expiry न होने पर नए खिलाड़ी का पहला अनुभव स्क्रीन पर marbles के जमा होते जाने जैसा बन जाता था
- active rigid bodies की संख्या बढ़ने से physics simulation भी धीमा पड़ता था
- marble expiry समय के साथ errors को जमा होने से रोकती है, और सिर्फ 30 सेकंड देखकर ज़्यादातर marbles कहाँ पहुँचते हैं, यह देखना संभव बनाती है, जिससे moderation आसान हो जाता है
पूरी मशीन को real time में न चलाने का तरीका
- Machine architecture की पहली बड़ी धारणा यह थी कि ऊपर दिए गए constraints का पालन हो तो अलग-अलग tiles को जोड़कर उन्हें पूरी मशीन जैसा दिखाया जा सकता है
- कुछ छोटे maps बनाकर और हल करके इस धारणा की पुष्टि की गई
- चूँकि पूरी मशीन को न server पर और न client पर real time में चलाया जा सकता था, इसलिए ऐसा तरीका चाहिए था जो केवल उपयोगकर्ता को दिख रहे क्षेत्र के आसपास simulation चलाए
- लक्ष्य यह था कि उपयोगकर्ता एक marble को मशीन के ऊपर से नीचे तक follow कर सके
-
physics की दुनिया जहाँ सिर्फ दिखने वाला हिस्सा मौजूद है
- शुरुआती map viewer सिर्फ visible area को simulate करता था, लेकिन scroll करने पर नए आने वाले tiles खाली अवस्था में शुरू होते थे, जिससे flow में gaps दिखते थे
- खाली tiles की जगह उन्हें पहले से active दिखाने के लिए stable state तक पहुँच चुके tiles के snapshot सहेजकर स्क्रीन में आने से ठीक पहले लोड करने का तरीका चुना गया
- अंतिम comic में वास्तव में physics simulation में वही tiles मौजूद रहती हैं जो render हो रही होती हैं
- स्क्रीन के ऊपर भी मशीन जारी है, ऐसा दिखाने के लिए simulation की top row tiles में input constraints की expected rate के मुताबिक marbles पैदा करके भेजे जाते हैं
-
approval के समय का snapshot
- snapshot generation moderation UI से जुड़ा हुआ है
- moderator को tile approve करने से पहले कम से कम 30 सेकंड इंतज़ार करना होता है, और approval button दबाने के उसी समय की state snapshot के रूप में सहेजी जाती है
- यदि device को अच्छा दिखने वाली स्थिति तक पहुँचने में थोड़ा और समय लगे, तो moderator के पास थोड़ा और इंतज़ार करने का विकल्प होता है
- snapshot तरीका accumulated errors को reset करने का प्रभाव देता है, इसलिए जब उपयोगकर्ता scroll करके कोई नई tile पहली बार देखता है, तो उसे वही साफ़ state दिखती है जिसे moderator ने अच्छा माना था
- लंबे समय तक देखते रहने पर कई devices रुक सकती हैं या टूटे हुए हाल में पहुँच सकती हैं, लेकिन आगे explore करने पर नई snapshots मिलती रहती हैं
- पूरी मशीन कभी पूरी तरह simulate नहीं होती, और नतीजा hyperreality के काफ़ी करीब पहुँचता है
React, DOM, और Rapier rendering structure
- Machine Rapier physics engine पर बनाई गई है
- Rapier के documentation, API, उपयोगी basic primitives, और Rust implementation के कारण WASM browser performance इसके फायदे थे
- शुरुआत में Rapier की determinism guarantee में भी रुचि थी, लेकिन अंततः server-side simulation नहीं किया गया
- Rapier के ऊपर custom React context
<PhysicsContext>लिखा गया- React component lifecycle के भीतर Rapier physics objects बनाए और manage किए गए
- हर placeable object और collision surface को “widget” component के रूप में develop करना आसान हो गया
- React एक तेज़ और rough scene graph की तरह काम करता है
- tile unmount होने पर उससे जुड़े physics objects और DOM साफ़ हो जाते हैं
- fast refresh के जरिए hot reloading से collision shapes को adjust करना आसान था
- physics hooks को ऐसा बनाया गया कि वे
<PhysicsContext>के बाहर काम न करें, जिससे moderation UI के static preview में मदद मिली - बाद में लगा कि Rapier objects को hooks की जगह components के रूप में बनाना बेहतर होता
- react-three-rapier यही तरीका अपनाता है, और यह React diffing के साथ बेहतर फिट बैठता है
useEffectआधारित तरीका dependency बदलने पर पुरानी instance हटाकर नई बना देता है
-
केवल DOM rendering
- Machine पूरी तरह DOM में render होती है
- शुरुआत में सोचा गया था कि performance limit आने पर PixiJS या canvas पर जाया जा सकता है, लेकिन पहले DOM approach को पूरी तरह आज़माया गया क्योंकि इसमें कम चीज़ें बनानी पड़ती थीं
- rendering performance के लिए frame loop physics simulation वाले widgets की style सीधे apply करता है
- React diff केवल तब चलता है जब scene graph की structure बदलती है
- शुरुआत में marbles भी React से render की गईं, लेकिन बार-बार create/delete होने से diff cost बढ़ी, इसलिए एक अलग optimized renderer बनाया गया
- screen के बाहर की marbles और widgets पर draw culling लागू की गई
- यह तरीका simulation में मौजूद 4,000 marbles और स्क्रीन पर दिखाई देने वाली सैकड़ों marbles के साथ अच्छी तरह चला, इसलिए DOM-only rendering को अंतिम रूप दिया गया
API, moderation, और submissions का संचालन
- backend davean और Kevin ने Haskell में लिखा, और storage के लिए Redis इस्तेमाल किया गया
- codebases के बीच type sharing के लिए OpenAPI और OpenAPI fetch का उपयोग किया गया
- शुरुआत में Haskell types के हिसाब से ढलना थोड़ा असुविधाजनक था
- लेकिन इससे अंतिम समय के API changes को coordinate करने में मदद मिली
- TanStack Query server push के बिना caching और auto-refresh संभालने में उपयोगी रही
-
moderation UI और priority
- Ed White द्वारा डिज़ाइन की गई moderation UI वह bottleneck थी जिससे हर submission को public होने से पहले गुजरना पड़ता था
- moderators को किसी खास tile के लिए सैकड़ों candidate designs में से चुनना पड़ सकता था
- queue priority इस तरह तय की गई कि widget type के हिसाब से interestingness score दिया गया, और हर instance को गिनकर candidate tiles sort की गईं
- यह तरीका ज़्यादा elements वाली solutions की ओर झुकता है, लेकिन moderators सूची के बीच तक जाकर अधिक minimal solutions से संतुलन बना लेते थे
- जमा हुई designs की संख्या और वास्तव में मशीन में publish हुई designs की संख्या के बीच बड़ा अंतर एक अफ़सोस की बात रहा
- launch से पहले backlog का ज़्यादा हिस्सा public करने के तरीके खोजे गए, लेकिन moderation time constraints के भीतर अच्छा संतुलन नहीं मिला
- live submissions खत्म होने के बाद submission dataset को और ज़्यादा साझा करने का तरीका खोजने की इच्छा है
-
approval cooldown और speed control
- क्योंकि tile snapshots की quality महत्वपूर्ण थी, moderator approval button तब तक disabled रहता था जब तक simulation कम से कम 30 सेकंड न चल जाए
- यह cooldown stable state का snapshot बनाने और यह जाँचने में मदद करता था कि output expected rate से marbles ले रहा है या नहीं
- शुरुआत में लगा था कि moderators को यह परेशान करेगा, लेकिन जल्दबाज़ी वाले फैसले रोकने के कारण इसे सकारात्मक रूप से लिया गया
- launch के बाद एक slider जोड़ा गया जिससे moderators simulation को real time से बहुत तेज़ चला सकते थे
- इस feature से submission के शुरुआती 30 सेकंड 5 सेकंड से कम में देखना संभव हुआ, और लंबे समय के behavior की समीक्षा भी आसान हुई
अनपेक्षित tile interactions
- “Jamslunt Interfoggle” public होने के पहले कुछ घंटों में आए devices में से एक था, और यह fan के संकरे range का उपयोग करने वाला mechanism है
- यह device नीली marbles को एक passage में इकट्ठा करता है, और जब वजन काफ़ी हो जाता है तो उन्हें दोनों ओर बहा देता है
- इसके ऊपर रखा गया “Bouncy” तीन-तरफ़ा crossing path के जरिए marbles फेंकने वाला chaos engine है
- Bouncy कभी-कभी हरी marble को गलत output में भेज देता है, और यह marble अटकी हुई नीली marbles के ढेर को तोड़कर Interfoggle में chain flow बना देती है
- editor में inputs को समझने में आसान रखने के लिए केवल सही रंग की marbles दी जाती थीं, इसलिए Interfoggle को इस हरी marble behavior को ध्यान में रखकर design नहीं किया जा सकता था
- ऐसे अनपेक्षित संयोजन shared canvas में लोगों द्वारा tools के रचनात्मक उपयोग के रूप में प्रोजेक्ट की बड़ी खुशियों में से एक बने
code और बाकी प्रयोग
- Machine का source code GitHub repository में देखा जा सकता है
- पूरी मशीन को global स्तर पर पूरी तरह simulate करने वाला implementation एक दिलचस्प hacking task के रूप में बाकी है
- Machine में सीधे design जोड़ने का लिंक xkcd 2916 पर है
1 टिप्पणियां
Hacker News की राय
यह पढ़कर मुझे हंसी आई, क्योंकि उस समय मुझे बिल्कुल पता नहीं था कि ऐसा कुछ हो रहा है
क्या चल रहा है, इसकी कोई व्याख्या भी नहीं दिखी, मुझे यह भी नहीं पता था कि यह सबके साथ मिलकर किया जाने वाला अनुभव है; बस ऐसा लगा कि कई random चीजें अव्यवस्थित तरीके से हो रही हैं
मैंने कुछ tiles पूरे करके submit किए, और यह सोचकर कि यही “अगले चरण” पर जाने का तरीका है, उन्हें “test 1b” जैसे बेवकूफाना नाम दे दिए। क्योंकि मुझे लगा यह single-player है, इसलिए नाम सिर्फ मुझे ही दिखेगा
कुछ बनाने के बाद मैं ऊब गया, इधर-उधर देखा तो जटिल चीजें दिखीं, लेकिन मुझे नहीं पता था कि वे submissions हैं; मैंने सोचा वे बस level solve करने के लिए starting points हैं। आखिरकार, कह सकते हैं कि मैं April Fools' prank में फंस गया
शायद इसलिए कि मैंने वह original machine game कभी नहीं खेला था जिससे यह inspired था :-)
लगता है मैंने बहुत सारे “bonk” elements जोड़ते-जोड़ते rapier को crash करा दिया
Uncaught Error: recursive use of an object detected which would lead to unsafe aliasing in rustat jt (rapier_wasm2d_bg.js:4836:11)at 4ea5626ea4b1e4145572.module.wasm:0xf061cat 4ea5626ea4b1e4145572.module.wasm:0xf0638at 4ea5626ea4b1e4145572.module.wasm:0xb5e7bat H.remove (rapier_wasm2d_bg.js:1051:14)at l.remove (collider_set.js:87:18)at y.removeCollider (world.js:343:28)at PhysicsContext.tsx:258:15फिर भी यह वाकई मजेदार है, और अफसोस है कि जब यह real-time में खुला था तब मुझे पता नहीं था। अच्छा होगा अगर लोगों द्वारा बनाई गई individual machines के लिए भी permanent links बन सकें
मुझे पता है storage की समस्या हो सकती है, लेकिन JSON को base64 encode करके URL parameters में डालने जैसा कुछ नहीं हो सकता? मैं अजीब maps बनाकर लोगों के साथ share करना चाहता हूं
जो machines पूरी public version में शामिल हुई हैं, उनके permanent links बन सकते हैं, लेकिन moderation queue से न चुनी गई individual creations के permanent links नहीं हैं
comic domain पर बिना review वाला user-generated content host करने के जोखिम से बचने के लिए यह फैसला जानबूझकर लिया गया था
संदर्भ के लिए, HN पर यह विषय 6 अप्रैल को भी आया था और उस पर 14 comments थे
https://news.ycombinator.com/item?id=39953514
“यह तय करने के लिए कोई खास incentive नहीं था कि stickers कहां लगाए जाएं। players के पास individual actions से plot को आगे बढ़ाने की पर्याप्त agency नहीं थी। इसलिए creativity समान stickers को tiles की तरह दोहराने या lines बनाने जैसे सरल patterns तक सीमित रह गई।”
आह, game तो बड़ी company की office life बन गया
जब यह public हुआ था, मैंने इसमें हिस्सा लिया। मुझे लगता है मैंने करीब एक घंटा इस पर लगाया कि सही ball सही output तक जितनी reliably हो सके पहुंच जाए
submit करने के बाद जब page refresh किया, तो उसी जगह किसी और का device था। मानना पड़ेगा, वह ज्यादा सुंदर था, लेकिन reliability कम थी
काश यह तरीका है, यह बात थोड़ा पहले बता दी गई होती। और लगता है मैं अकेला नहीं था जिसे पता नहीं था कि building blocks की list scrollable है
अब वापस जाकर check करने की energy नहीं है :(
वाह, मैं और मेरे दोस्त ने भी 2014 में यही idea सोचा था और Ludum Dare के लिए implement किया था। https://nickfa.ro/wiki/CoinSlot
उस idea को और polished और अच्छी तरह काम करने वाले रूप में देखकर अच्छा लगा
यह मुझे मेरी जवानी की याद दिलाता है। मैंने इस पर सचमुच बहुत सारा समय बहुत खुशी से बर्बाद किया था
https://www.myabandonware.com/game/the-incredible-machine-1m...
शायद मैं कुछ miss कर रहा हूं, लेकिन machine के अंदर कुछ elements सिर्फ खास रंग की balls पर ही असर करते हुए क्यों दिखते हैं?
मेरा अनुमान है कि यह रंगों को पूरी तरह mix होने से रोकने की व्यवस्था है, लेकिन लगता है इस post में इसे explain नहीं किया गया
yellow ball हल्की है और उस पर air resistance ज्यादा है, green ball भारी है, और red ball बहुत ज्यादा bounce करती है
इसलिए आप physical sorter design कर सकते हैं
काश यह आसानी से पता लगाने का तरीका होता कि मेरी बनाई machines में से कोई final version में गई है या नहीं
अगले design में अगर पहले के submissions के titles को local storage जैसी जगह में save करके notification दिखाया जा सके, तो अच्छा रहेगा