<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aryaman Godara</title>
    <description>The latest articles on DEV Community by Aryaman Godara (@aryamangodara).</description>
    <link>https://dev.to/aryamangodara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F811299%2Fd3e73002-3121-47ca-ba58-600380c084bd.jpeg</url>
      <title>DEV Community: Aryaman Godara</title>
      <link>https://dev.to/aryamangodara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aryamangodara"/>
    <language>en</language>
    <item>
      <title>Your Phone Is the Joystick: I Turned a Browser Tab Into a Game Console</title>
      <dc:creator>Aryaman Godara</dc:creator>
      <pubDate>Sun, 28 Jun 2026 16:22:54 +0000</pubDate>
      <link>https://dev.to/aryamangodara/your-phone-is-the-joystick-i-turned-a-browser-tab-into-a-game-console-3dhi</link>
      <guid>https://dev.to/aryamangodara/your-phone-is-the-joystick-i-turned-a-browser-tab-into-a-game-console-3dhi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmzy8he2igf43ao63r89q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmzy8he2igf43ao63r89q.png" alt="Cover Image" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open a Snake game on your laptop. Scan the QR code with your phone. Your phone is now the joystick — push the stick further and the snake turns &lt;em&gt;and&lt;/em&gt; speeds up. No app install, no Bluetooth, no pairing menu.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frnx3l6xpgje17o37kila.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frnx3l6xpgje17o37kila.gif" alt="demo GIF" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Play it now:&lt;/strong&gt; open &lt;a href="https://go-console-84748.web.app/" rel="noopener noreferrer"&gt;go-console-84748.web.app&lt;/a&gt; on a laptop and scan the QR with your phone. (Repo link at the bottom.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — A cross-device Snake game. The desktop browser hosts and renders the game; the phone is a wireless analog joystick. They link with a 6-digit code or QR. Stack: &lt;strong&gt;vanilla JS, no bundler, no build step&lt;/strong&gt;, plus &lt;strong&gt;two Firebase databases&lt;/strong&gt; (Firestore for state, Realtime DB for the joystick&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." alt="Uploading image" width="800" height="400"&gt;&lt;/a&gt; stream). Last snake standing, up to 3 players today — the engine and rules are already wired for 6.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I wanted "couch co-op" energy without making anyone download an app. Everyone already has a phone with a touchscreen and a browser. Why is connecting it to the big screen still a chore? So I made the phone the gamepad and the laptop the console — and the only thing standing between you and playing is a glance at a QR code.&lt;/p&gt;
&lt;h2&gt;
  
  
  How the two devices talk
&lt;/h2&gt;

&lt;p&gt;One &lt;code&gt;index.html&lt;/code&gt; holds &lt;strong&gt;both&lt;/strong&gt; roles. Which one you get is decided at load: wide screen → host, or a &lt;code&gt;?session=&lt;/code&gt; URL param (what the QR encodes) → controller. Same file, two completely different apps.&lt;/p&gt;

&lt;p&gt;The data flow splits cleanly by &lt;em&gt;update frequency&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desktop (host):&lt;/strong&gt; owns the game loop, physics, collision, rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phone (controller):&lt;/strong&gt; captures the joystick, streams input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pairing:&lt;/strong&gt; 6-digit code / QR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the databases split the same way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firestore&lt;/strong&gt; — &lt;code&gt;sessions/{code}&lt;/code&gt;: session metadata, score, one-shot actions (start/restart). Written only on state &lt;em&gt;edges&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realtime Database&lt;/strong&gt; — &lt;code&gt;controllers/{code}&lt;/code&gt;: the high-frequency (~30Hz, throttled) joystick stream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That hybrid is the one architecture decision I'd defend hardest. Firestore is great for sparse, structured writes; it is &lt;em&gt;not&lt;/em&gt; where you want to fire 30 joystick updates a second per player. RTDB is. Splitting the control plane from the data plane is what keeps a multiplayer match sane.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deep dive 1: the joystick is analog, not 4-way
&lt;/h2&gt;

&lt;p&gt;Classic Snake snaps to four directions. This one steers at &lt;em&gt;any&lt;/em&gt; angle. The joystick maps to a heading (radians) &lt;strong&gt;and&lt;/strong&gt; a speed boost scaled by how far you push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;joystickToControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseSpeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;magnitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hypot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;magnitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;targetDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseSpeed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxSpeedBoost&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;targetDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseSpeed&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;atan2&lt;/code&gt; gives the heading; &lt;code&gt;magnitude&lt;/code&gt; doubles as the throttle. Push gently to ease around a corner, slam the stick to sprint. It's a tiny pure function (lives in &lt;code&gt;logic.js&lt;/code&gt;, fully unit-tested), but it changes the entire feel of the game.&lt;/p&gt;

&lt;p&gt;One more trick: I don't re-send the joystick vector if it hasn't really moved. An epsilon change-gate (&lt;code&gt;shouldSendJoystick&lt;/code&gt;) means a steady-held stick stops re-sending entirely — noticeably cutting joystick writes without dropping a single real input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive 2: a combo system where the visual &lt;em&gt;is&lt;/em&gt; the truth
&lt;/h2&gt;

&lt;p&gt;Eat again within the combo window (4.5s) and your multiplier climbs (capped at 6×). Score per food = &lt;code&gt;10 × multiplier&lt;/code&gt;. The yellow combo badge drains via a CSS animation that runs for exactly &lt;code&gt;comboWindowMs&lt;/code&gt; — so the bar you watch emptying is the &lt;em&gt;same&lt;/em&gt; duration as the actual expiry logic, which is checked each play frame in the game loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastFoodTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;gameConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comboWindowMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;updateComboDisplay&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same &lt;code&gt;comboJuice()&lt;/code&gt; curve drives particles, screen shake, and food-sound pitch, so a 3× combo &lt;em&gt;looks and sounds&lt;/em&gt; identical in solo and multiplayer. Speaking of sound — every SFX is synthesized at runtime with the Web Audio API (no asset files), and the food pitch rises with the combo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;playFoodSound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;660&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.06&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;playTone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;square&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.28&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 6× streak literally sounds triumphant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive 3: no bundler, no modules — and a CI guard to keep me honest
&lt;/h2&gt;

&lt;p&gt;There's no bundler and no ES modules. 19 plain &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; files load in a fixed order into &lt;strong&gt;one global scope&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/utils.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/logic.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/config.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- … strict order … --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployed code is exactly what I wrote, just concatenated by the browser — no tree-shaking surprises, no source-map archaeology. The catch: rename a function and you can silently break a consumer in another file. So &lt;code&gt;no-undef&lt;/code&gt; is on, every cross-file global is declared in &lt;code&gt;.eslintrc.json&lt;/code&gt;, and a CI script (&lt;code&gt;check:globals&lt;/code&gt;) fails the build if that list &lt;strong&gt;drifts in either direction&lt;/strong&gt; from the actual top-level declarations. The failure is the feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas (the stuff that actually bit me)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS Safari has no &lt;code&gt;navigator.vibrate&lt;/code&gt;.&lt;/strong&gt; It never has. So haptics fall back to an old Safari trick: a hidden &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; wrapping an &lt;code&gt;&amp;lt;input type="checkbox"&amp;gt;&lt;/code&gt; with the &lt;code&gt;switch&lt;/code&gt; attribute, then programmatically clicking the label to nudge the OS haptic engine. If even that fails, a red &lt;code&gt;.loss-flash&lt;/code&gt; CSS cue plays everywhere instead. Feature-detect, degrade, never throw into gameplay:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vibrate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vibrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// iOS shim: a hidden &amp;lt;label&amp;gt; + checkbox-with-`switch` can poke the haptic engine&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;switch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The service worker stale-cache trap.&lt;/strong&gt; An earlier "network-first HTML + stale assets" strategy let fresh markup load against a &lt;em&gt;cached&lt;/em&gt; &lt;code&gt;controller.js&lt;/code&gt;, breaking event handlers until a second reload. Going &lt;strong&gt;network-first for everything same-origin&lt;/strong&gt; closed the gap. &lt;code&gt;const CACHE = 'snake-shell-v31'&lt;/code&gt; — that v31 is 31 times I changed shell assets and had to bump the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics off by default.&lt;/strong&gt; GA4 only boots after an explicit opt-in, and &lt;code&gt;navigator.doNotTrack&lt;/code&gt; / &lt;code&gt;globalPrivacyControl&lt;/code&gt; auto-decline. No consent → no handle, no cookies, no gtag. One &lt;code&gt;trackEvent()&lt;/code&gt; helper no-ops when analytics is unavailable, so a privacy setting can never crash the game.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplayer races.&lt;/strong&gt; Two phones grabbing the last slot at once → a Firestore transaction serializes them; nobody double-claims. A rejoin token in &lt;code&gt;localStorage&lt;/code&gt; lets a phone that refreshes mid-round recover its snake, and &lt;code&gt;onDisconnect&lt;/code&gt; only nukes the host's &lt;code&gt;_host&lt;/code&gt; child, never live players.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it / what's next
&lt;/h2&gt;

&lt;p&gt;Grab a friend (or two). Open it on a laptop, scan with your phone, and go for the high score — or start a &lt;strong&gt;last-snake-standing&lt;/strong&gt; match. The live cap is 3 players right now; the simulation and security rules already accept p1–p6, so flipping one &lt;code&gt;maxPlayers&lt;/code&gt; knob is the only change between here and a six-snake arena.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;▶ Play:&lt;/strong&gt; &lt;a href="https://go-console-84748.web.app/" rel="noopener noreferrer"&gt;go-console-84748.web.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/aryamangodara/snake_controller" rel="noopener noreferrer"&gt;github.com/aryamangodara/snake_controller&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The phone-as-gamepad pattern feels underused. &lt;strong&gt;What other browser-only hardware tricks have you pulled off without shipping a native app?&lt;/strong&gt; Drop them in the comments — I'll be replying all day.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Pass Microsoft Az:900(Fundamentals) in 5 hours: Step by Step Guide</title>
      <dc:creator>Aryaman Godara</dc:creator>
      <pubDate>Sat, 25 Mar 2023 06:15:25 +0000</pubDate>
      <link>https://dev.to/aryamangodara/pass-microsoft-az900fundamentals-in-5-hours-step-by-step-guide-4bd6</link>
      <guid>https://dev.to/aryamangodara/pass-microsoft-az900fundamentals-in-5-hours-step-by-step-guide-4bd6</guid>
      <description>&lt;h2&gt;
  
  
  💝Intro
&lt;/h2&gt;

&lt;p&gt;So, you clicked this and which clearly states that you are interested in the cloud industry. With these overwhelming tools and technology, you are confused about where to start.&lt;br&gt;
No worries, this 20-year-old depressed kid will rescue you.😶&amp;nbsp;. (Yeah, bro I got 900 marks.)&lt;br&gt;
If you choose azure it is the best practice to start with fundamentals certification even if you have experience of 5 years or more in the industry and you might have the basic idea of cloud. Then also I would suggest you go for Azure Fundamentals certification.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤓 Let's Learn how to pass and get the certification.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v6znq3Gr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8z75l45o55v0920k7ya.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v6znq3Gr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8z75l45o55v0920k7ya.gif" alt="Image description" width="480" height="270"&gt;&lt;/a&gt;&lt;br&gt;
Let's dive right in… and fail professionally&lt;/p&gt;

&lt;p&gt;These steps might not work for you so please don't feel discouraged or something like that but also remember that these worked perfectly for me. I have conducted this experiment on myself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Head directly to Microsoft's official website for AZ 900. (not now geek, calm down read it first..)
Read it thoroughly (And I mean it, thoroughly). You will get a basic idea of what the exam expects from you and also the latest study guides and exam overview.
Don't worry about seeing the price of the certification exam. I know you are very poor and you would like to save that money for your outing with your friends🤡. I will tell you the scheme for it also.
Complete all the learning paths recommend to you by Microsoft. You will earn trophies also which might boost your learning process. (Frankly, these trophies are dumb and don't do anything😂)
Now you attend the free virtual training of Microsoft Fundamentals for two days. After these two days, you will get a free exam voucher.
You can now make an account on Pearson Vue which will give you a certification id and a chance to appear for an exam(for free).
With all these 'Guru Mantras' you will succeed for sure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📺Video?&lt;br&gt;
Some people might argue that watching the YouTube playlist and then directly appearing for an exam is the 'best way'. I strongly disagree with that point. I think you should give the shot to official documentation first, because those guys made that product, and obviously questions will be from their documentation.&lt;br&gt;
Plus it is very easy to read.&lt;br&gt;
Plus, it is free.&lt;br&gt;
Plus YouTube videos kill your time in many ways first they rant their introduction and then they beg for like, share, and subscribe( I will also, but at the end only… 😭)&lt;br&gt;
Plus videos are also available in the documentation&lt;br&gt;
Plus you get to read in this content-world&lt;br&gt;
Plus reading official docs make you feel like a geek 😊. You think of yourself as a tough competitor of Mark Zuckerberg&lt;br&gt;
🔥Too hot to&amp;nbsp;handle&lt;br&gt;
Now you are as hot as fire and ready to smash the cloud industry. Don't let the fire inside you go off without burning anything. Prepare for Azure fundamentals now!&lt;br&gt;
Remember, It is very tough to get a cloud job as a fresher. This is very sad, I know. I can risk it. Can you?&lt;br&gt;
If you think I have helped you anyhow. Please smash the celebrate👏 button to increase my reach. It really helps. I am new to blogging.&lt;/p&gt;

&lt;p&gt;My reaction when I got the AZ 900 certificate (failed professionally&amp;nbsp;?)&lt;/p&gt;

&lt;p&gt;If Microsoft is reading it… Hire me immediately&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why I open sourced my flutter game application?</title>
      <dc:creator>Aryaman Godara</dc:creator>
      <pubDate>Tue, 08 Feb 2022 08:53:16 +0000</pubDate>
      <link>https://dev.to/aryamangodara/why-i-open-sourced-my-flutter-game-application-4pib</link>
      <guid>https://dev.to/aryamangodara/why-i-open-sourced-my-flutter-game-application-4pib</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;May the source be with you &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am the founder of the app &lt;a href="https://play.google.com/store/apps/details?id=com.aryaman.arcade_plaza&amp;amp;hl=en_IN&amp;amp;gl=US"&gt;&lt;em&gt;Arcade Plaza.&lt;/em&gt;&lt;/a&gt; This is Flutter based application currently published exclusively on Play Store. My app was running very successfully some months &lt;strong&gt;before I started messing with it&lt;/strong&gt;. Although I had experience of running a app over 500k active users but this was a disaster 🤐.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aryamangodara/Arcade-Plaza"&gt;&lt;em&gt;Now I have open sourced it here&lt;/em&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, lets start it from the beginning ..&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  😶‍🌫️ What does this app do ?
&lt;/h2&gt;

&lt;p&gt;This app let us play free online arcade games at one stop. Conventionally users have to go to the different websites across the internet to find out the suitable game for themselves. Also, there are few websites which are just spam and contains harmful content or low quality games. Obviously it is very inconvenient to go to chrome and play your favorite game(s).&lt;/p&gt;

&lt;p&gt;We keep list of handpicked games from the web and let users play them or mark them as their favorite to play later. Games are of high quality indeed &lt;strong&gt;such as Stacky Run, Ludo, Cut the Rope and many other games developed by major gaming companies&lt;/strong&gt;. &lt;br&gt;
Quality is my first priority ✌️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.aryaman.arcade_plaza&amp;amp;hl=en_IN&amp;amp;gl=US"&gt;Download our free app to know more&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 How this idea came to my mind ?
&lt;/h2&gt;

&lt;p&gt;I was just scrolling through the play store and I thought there is no proper app where I can play whichever game that I desire to play &lt;em&gt;(which satisfies my mood 🌹)&lt;/em&gt; without downloading the various games on my mobile as it turns out they make functioning of mobile slow and occupies large disk space ( My game is only of 7 M.B. )&lt;br&gt;
So I thought to make this app where &lt;strong&gt;we can find more than 75 games at one place&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  💹 App at its peak
&lt;/h2&gt;

&lt;p&gt;To be very honest with you, I started &lt;em&gt;this as a joke🥲&lt;/em&gt;. I made a separate developer account as I was not very serious with it and didn't want to destroy my existing account, but to my amaze it turned out to be very successful. On the first day it receives 10 downloads on the next day 257 downloads.(Note that I have not spent even a single penny for its marketing or advertisement).So &lt;strong&gt;within the the week I had 900+ users.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💲 Earnings
&lt;/h2&gt;

&lt;p&gt;As you might be already knowing that its against the policy of the Facebook Ads to share the official earnings. Still, you can easily estimate the earnings 😏 There was a banner ad with &lt;strong&gt;average of 970 impressions daily...&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📉 Downfall and reason of open sourcing
&lt;/h2&gt;

&lt;p&gt;I was very happy with my app's performance but I added some necessary like a favorite button on the each game tile. A friend of mine suggested me to partner with a company named "Gamezop", &lt;strong&gt;which was the biggest mistake !&lt;/strong&gt; (I still hate that friend 🙄)&lt;br&gt;
I added games of Gamezop and the ads which users will see, I will receive 50 percent of the revenue produced by those games. In my greed I placed them on the top instead of those existing better games. Gamezop games are average but there are ton of ads. Even playing a single level can frustrate user and as they were on the top so users were not able to scroll more to find other games. In frustration people started uninstalling in great numbers. It is now reached to a state where I am getting 6-7 installs and 11 uninstalls monthly. It is shrank to 60 users only. So I need a community of developers to help a novice like me...&lt;/p&gt;

&lt;h2&gt;
  
  
  ⭐ Response and Reviews
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Touchwood all of the reviews are of 5 stars and with a positive response till date&lt;/strong&gt;. You can see it for yourself people at play store are loving it 💝. This also played a major role in the initial success of it too because Play Store's algorithm promotes the app with positive responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 How open source is going up to now
&lt;/h2&gt;

&lt;p&gt;Its too early to comment on it but there are no response except mine. Although there are few stars on the repo. Sadly my friend's repo is very cool (yeah, the same friend).&lt;strong&gt;I thought that the moment I will upload it I will get PR's(Pull Requests) but reality hits hard 🥲&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, this was my first tech blog. I request you to like and spread so that I will be motivated to write more. Tell me or ask me something in the comments section 😊.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>dart</category>
    </item>
  </channel>
</rss>
