240 ब्राउज़र टैब में चलने वाला Pong
(eieio.games)- बंद न किए गए tab bar की खाली जगह को स्क्रीन की तरह इस्तेमाल करते हुए, Chrome के 240 टैब के favicon को पिक्सेल के रूप में उपयोग करने वाला Pong प्रयोग बनाया गया
- 8×30 टैब ग्रिड AppleScript से बनाया गया, और हर टैब URL query में मौजूद
windowIndexऔरtabIndexसे अपनी position निकालता है - background tabs में
setIntervalलगभग 1 सेकंड के अंतराल तक धीमा हो गया था, लेकिन timer को Web Worker में ले जाने पर favicon refresh पर्याप्त तेज़ हो गया - server WebSocket की जगह Broadcast Channel API से समान domain वाले टैब synchronize किए गए, और सभी टैब registration पूरी होने के बाद main tab ने animation शुरू किया
- सबसे बड़ा bottleneck हर frame में सैकड़ों favicon दोबारा बनाना था; सिर्फ वास्तव में बदले हुए टैब update करने पर animation काफी ज़्यादा smooth हो गया
240 टैब को एक स्क्रीन की तरह इस्तेमाल करने का तरीका
- यह project बंद न किए गए browser tabs द्वारा घेर ली गई जगह का उपयोग करके Pong चलाने का एक प्रयोग है
- स्क्रीन Chrome की 8 windows और हर window में 30 tabs से बने 8×30 tab grid से बनी है
- ball और paddles सामने वाली window के HTML canvas और ऊपर के tab bar के favicon area के बीच लगातार move करते हैं
- code GitHub पर faviconic में公開 है, और इसके साथ prototype स्थिति का “awful” code होने की चेतावनी दी गई है
Flappy Favi से शुरू हुआ idea
- प्रेरणा Tru द्वारा बनाए गए favicon के अंदर चलने वाले Flappy Bird version Flappy Favi से मिली
- favicon browser tab bar में दिखने वाली छोटी image होती है, लेकिन एक ही favicon बहुत छोटा होता है और देखना मुश्किल होता है
- कई tabs में फैली image draw करने के लिए पहले तीन समस्याएं हल करनी थीं
- tabs को देखने में अच्छा लगने वाले grid में arrange करने का तरीका
- background tabs में भी favicon को तेज़ी से update करने का तरीका
- कई tabs की screen को आपस में synchronize करने का तरीका
AppleScript से Chrome tab grid बनाना
- शुरुआत में Chrome का new tab button बार-बार दबाकर tabs को छोटा किया गया, फिर दूसरी window को align करके दूसरी row बनाई गई, ताकि feasibility देखी जा सके
- बड़ा grid manually बनाना झंझट भरा था, इसलिए इसे AppleScript से automate किया गया
- script Chrome की 8 windows खोलती है, हर window में 30 tabs बनाती है, और windows को इस तरह position करती है कि वे एक-दूसरे के ऊपर stack हों
- Chrome द्वारा बंद किए गए tabs को फिर से खोलने की कोशिश करने की समस्या थी, इसलिए script शुरू करते समय इसे साफ करना पड़ा
- हर tab URL में
windowIndexऔरtabIndexशामिल होते हैं- आखिरी खुले tab में
isMain=true,numWindows,numTabs,fullWidthजैसे अतिरिक्त parameters लगते हैं
- आखिरी खुले tab में
background tabs में favicon को तेज़ी से refresh करना
- browser default favicon URL ढूंढता है, लेकिन HTML
headमें favicon location बताने वाला element जोड़कर और उसे update करके icon बदला जा सकता है - Chrome में favicon refresh अनुभव के अनुसार लगभग 4 बार प्रति सेकंड के स्तर का है
- background tabs में resource usage सीमित होता है, इसलिए 250ms पर favicon बदलने वाला छोटा loop भी लगभग 1 सेकंड में सिर्फ एक बार चल रहा था
- Web Audio API के background audio callback को workaround के तौर पर इस्तेमाल करने की कोशिश की गई, लेकिन वह ठीक से काम नहीं कर पाया
- बाद में timer को Web Worker में ले जाकर, worker से main document को message भेजकर favicon update कराने पर समस्या हल हो गई
- example structure में worker emoji को
OffscreenCanvasपर draw करता है, उसेdata URLमें convert करता है, और फिर main document को भेजता है- main document
link rel="shortcut icon"केhrefको बदलकर असल favicon update करता है
- main document
Broadcast Channel से tab registration और synchronization संभालना
- tabs के बीच communication में दो चुनौतियां थीं
- हर tab को पता होना चाहिए कि वह किस number की window का किस number का tab है
- tab updates भेजने के लिए कौन सा communication channel इस्तेमाल करना है, यह तय करना था
- position पता करना AppleScript द्वारा डाले गए URL query parameters से
windowIndexऔरtabIndexपढ़कर हल किया गया - शुरुआती proof of concept में हर tab WebSocket server से connect करता था और server images भेजता था
- WebSocket तरीका project के लक्ष्य से अच्छी तरह मेल नहीं खाता था
- server के बिना दुनिया भर के browser tabs में deploy करना चाहने वाली aesthetic वजह से टकराता था
- हर tab का connection समय अलग होता था, इसलिए updates synchronized नहीं रहते थे
- इसके बाद समान domain के कई tabs तक जानकारी पहुंचाने वाले Broadcast Channel API पर switch किया गया
- background tab अपने tab/window index वाला
registrationmessage broadcast channel पर भेजता है, और main tab उसे पाकरackलौटाता है - main tab सभी background tabs की registration मिलने के बाद animation शुरू करता है
canvas और favicon की alignment मिलाना
- सामने वाली window में draw की गई objects ऐसी दिखें जैसे वे tab bar में “घुस” रही हों, इसके लिए असल canvas और ऊपर के favicons को एक extended canvas की तरह treat किया गया
- implementation में Chrome window के actual dimensions खुद मापने पड़े
- Chrome window के left edge से पहले favicon तक 92 pixels
- favicon के bottom से actual window top तक 58 pixels
- favicon size 16×16
- इन measurements और tabs/windows की संख्या का उपयोग करके तीन values calculate की गईं
- favicon से align होने के लिए display किए जाने वाले canvas की width
- हर tab की width
- favicon row, URL bar और actual canvas समेत पूरे “canvas” की width और height
- अगर rectangle URL bar के नीचे है तो उसे actual canvas पर draw किया जाता है, और अगर URL bar के ऊपर है तो उस area की जानकारी दूसरे tabs को broadcast की जाती है
- हर tab अपने pixel coordinates calculate करने के बाद favicon को white/black pixels से update करता है
bottleneck data transfer नहीं, favicon generation था
- शुरुआती implementation चल तो रहा था, लेकिन canvas animation अटक रहा था, और
requestAnimationFrameके अंदर draw करने पर भी smooth नहीं था - शुरू में शक था कि main thread सभी favicon pixel states calculate करके broadcast channel में डालता है, और सैकड़ों tabs उसे पढ़ते हैं—इस structure में बहुत अधिक data copy हो रहा है
- सिर्फ rectangle की position broadcast करके हर tab से खुद intersection calculate कराने में बदला गया, लेकिन performance improve नहीं हुई
- असली समस्या यह थी कि हर tab हर frame में 4×4 black-and-white favicon image दोबारा बनाता था और
toDataURL("image/png")से URL generate करता था - result canvas बदला न हो, फिर भी सैकड़ों tabs प्रति सेकंड कई बार छोटे white favicons लगातार regenerate कर रहे थे
- favicon सचमुच बदलने पर ही update करने में बदलने से performance काफी बेहतर हो गई
- हालांकि दूसरे tabs का काम सामने वाले tab के animation को इतना धीमा क्यों बना रहा था, यह स्पष्ट रूप से समझे बिना ही रह गया
Snake की जगह Pong चुनने की वजह
- rectangle movement experiment के बाद छोटा “engine” साफ किया गया, और उसके ऊपर बनाने के लिए game चुना गया
- पहला candidate Snake था, क्योंकि वह block-based है और favicon के साथ अच्छी तरह fit हो सकता था
- लेकिन Snake बहुत ज्यादा block-based था, इसलिए माना गया कि front canvas की continuous movement का tab bar में discrete pixels में बदलने वाला effect उतना उभरकर नहीं आएगा
- अंततः Pong चुना गया
- ball और paddles को canvas और tab bar के बीच अक्सर आना-जाना पड़ता है
- यह movement project के visual effect से अच्छी तरह मेल खाता है
Pong implementation notes
- favicon drawing API तैयार होने के बाद Pong implementation खुद अपेक्षाकृत सरल था
- right-side computer player हमेशा paddle के center को ball के center से मिलाने की कोशिश करता है
- ball के paddle से bounce होने का angle paddle center के साथ relative position का उपयोग करने वाली simple trigonometry से calculate किया जाता है
- यह तरीका realistic नहीं है, लेकिन ball के हमेशा उसी angle पर बने रहने की समस्या रोक देता है
- collision detection के लिए दो rectangles intersect करते हैं या नहीं, यह check करने वाला function इस्तेमाल किया गया
- ball और paddles के favicon में smoothly प्रवेश करने वाले effect को highlight करने के लिए ball में trail जोड़ा गया
Recurse Center में बनाया गया चौथा game
- project Recurse Center batch के दौरान लिखा गया
- Recurse Center को programming के लिए free writer’s retreat जैसी जगह के रूप में introduce किया गया है
- यह project लगभग 40 दिनों में बनाया गया चौथा game है
- इच्छुक readers को Recurse Center में apply करने पर विचार करने के लिए कहा गया है
1 टिप्पणियां
Hacker News की रायें
नमस्ते! यह मैंने बनाया है। सोच रहा था कि HN पाठकों को यह पसंद आएगा या नहीं :)
अगर कोई सवाल हों तो जवाब दूंगा। कुछ बिखरे हुए विचार लिखूं तो, animated favicon इस्तेमाल करने पर यह कैसा दिखेगा, यह जानने की मुझे सच में उत्सुकता है। Firefox animated favicon को support करता है, इसलिए मसलन गेंद की future position predict करके animated SVG बनाया जाए तो शायद कहीं बेहतर frame rate मिल सके।
एक दोस्त ने offline बताया कि canvas rasterization आम तौर पर GPU पर handle होता है, इसलिए अटकी-अटकी animation देखकर जो मेरी performance intuition बनी थी, वह शायद उतनी सही नहीं थी।
Chrome favicon updates को प्रति सेकंड 4 बार तक सीमित करता है, इस पर मुझे कुछ हद तक ही भरोसा है। favicon refresh करने के कई तरीके हैं, इसलिए हो सकता है मैंने कुछ miss किया हो
[0] https://jakesgordon.com/writing/javascript-pong/part1/
1 Million Checkboxes के बाद यह, global cap locks, bad apple regex, uuid वगैरह—इतना momentum प्रभावशाली है
मैंने Nolen की हाल की Recurse presentation देखी, और इस तरह के पूरी तरह पागलपन भरे लेकिन मूल रूप से मजेदार और शानदार one-off games सच में बहुत आनंद देते हैं।
पुराने internet की याद आती है। वह दौर जब लोग बस silly और fun करने के लिए कुछ बना देते थे। कल रात उनकी पोस्टों से प्रेरित होकर, मैंने BEAM decompilation और कुछ tricks से एक बेहद मजेदार quasi-quine बनाया, जिसमें page अपना ही source code output करता है।
काश मेरे पास भी ऐसी चीजें लगातार निकालने का समय होता, और सिर्फ यह बात कि लोग इस तरह लगातार दिलचस्प चीजें बना रहे हैं, मुझे हंसा देती है।
जो लोग हंसना चाहें, उनके लिए वह sort-of quine हिस्सा यहां है: https://github.com/notactuallytreyanastasio/blog/blob/main/l...
Nolen जो भी बनाते हैं, अच्छा ही होता है। मेरे लिए वे single-purpose apps/sites के उस sweet spot को ठीक-ठीक छूते दिखते हैं, जो पुराने internet के बारे में nostalgia जगाता है
Matthew Rayfield का एक मिलता-जुलता experiment भी है, जिसमें tab favicon के बजाय address bar इस्तेमाल किया गया था: https://www.youtube.com/watch?v=q7GtCLwTmV4
इसे बनाते समय उनका URL वाला काम मेरे दिमाग में जरूर था, लेकिन सच कहूं तो blog पढ़े या video देखे काफी समय हो गया था। project के बीच में दोबारा देखते हुए यह याद आया कि वे भी “windows को stack करके 2D बनाना” वाली दिशा में गए थे—यह सचमुच मजेदार था
यह याद आता है:
“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
मैं दावे से कहता हूं कि अगला Doom है
साथ ही हर frame में screen का ज्यादा हिस्सा बदलने की संभावना अधिक है, इसलिए favicon update करने के लिए कहीं ज्यादा “canvas को data URL में बदलना” पड़ेगा। मौजूदा implementation में यह महंगा है। हालांकि मुझे OffscreenCanvas के बारे में पता नहीं था, और शायद यह मदद कर सकता है
Firefox में tabs ज्यादा narrow हो जाने की समस्या हो तो, अब भी Firefox browser को खुद inspect करके minimum tab width की style rule ढूंढी जा सकती है और उसे userChrome.css में डाला जा सकता है।
इससे जुड़े कुछ guides हैं, और tab width के लिए शायद किसी ने पहले से संकलन कर रखा होगा।
हालांकि यह cheat भी माना जा सकता है।
browser.tabs.tabMinWidthभी है, लेकिन मेरी याद में वह फिर भी minimum value enforce करता हैband Ok Go ने पहले Google Chrome के साथ मिलकर एक music video बनाया था, जिसमें browser windows और dancers बहुत शानदार तरीके से synchronize थे और kaleidoscope जैसे effects भी दिखे थे। इसे देखकर मुझे वही याद आया
https://www.youtube.com/watch?v=ISL1GfXwr-o