<?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: zzzu2221</title>
    <description>The latest articles on DEV Community by zzzu2221 (@zzzu2221).</description>
    <link>https://dev.to/zzzu2221</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%2F4073395%2F85be565e-bb3e-4714-b827-272a9644fcf8.png</url>
      <title>DEV Community: zzzu2221</title>
      <link>https://dev.to/zzzu2221</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zzzu2221"/>
    <language>en</language>
    <item>
      <title>What I Checked Before Shipping a Browser Game to itch.io</title>
      <dc:creator>zzzu2221</dc:creator>
      <pubDate>Tue, 18 Aug 2026 05:35:20 +0000</pubDate>
      <link>https://dev.to/zzzu2221/what-i-checked-before-shipping-a-browser-game-to-itchio-4506</link>
      <guid>https://dev.to/zzzu2221/what-i-checked-before-shipping-a-browser-game-to-itchio-4506</guid>
      <description>&lt;p&gt;I uploaded a small first-person sci-fi demo to itch.io this week. The build already ran in a normal browser, so I expected the upload to be the easy part.&lt;/p&gt;

&lt;p&gt;It was not difficult, but it did force me to separate “works on my machine” from “someone else can actually play this.” A browser game inside an itch.io page has a few extra ways to fail: the iframe can lose focus, a remote asset can stall, and a refresh can expose state that was never properly reset.&lt;/p&gt;

&lt;p&gt;Here is the checklist I used before calling the build public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start from a clean page
&lt;/h2&gt;

&lt;p&gt;My first pass was deliberately boring. I opened the game from its public page, clicked into the frame, and checked every control shown in the instructions: movement, sprint, jump, interaction, universe switching, and the memory log.&lt;/p&gt;

&lt;p&gt;That click into the frame matters. Keyboard input that works perfectly in a local tab may do nothing until the embedded game has focus. A player will not diagnose that problem; they will assume the game is broken and leave.&lt;/p&gt;

&lt;p&gt;I also tried resizing the window and returning to the page after switching tabs. The goal was not to cover every browser combination. I just wanted to catch the obvious cases that make a playable build feel dead on arrival.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refresh in the middle, not only at the beginning
&lt;/h2&gt;

&lt;p&gt;A fresh load is the easiest state a game will ever see. I refreshed while moving through the first area, after interacting with an object, and after switching between the available universes.&lt;/p&gt;

&lt;p&gt;For each refresh I checked three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the player started from a valid position;&lt;/li&gt;
&lt;li&gt;old input handlers were not duplicated;&lt;/li&gt;
&lt;li&gt;the UI and game state agreed about what was available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Restart bugs are easy to miss in short demos because the first run looks fine. The second run is where duplicated timers, stale flags, and half-reset objects usually become visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the network assumptions
&lt;/h2&gt;

&lt;p&gt;This build uses Three.js and two background music files that are loaded remotely. That keeps the package small, but it also means the game is not truly self-contained.&lt;/p&gt;

&lt;p&gt;I tested the public build from a fresh tab instead of trusting cached files from development. I also made the dependency clear on the game page. If the network is slow, a player should know whether the game is loading or has failed; silence and a blank frame are not useful feedback.&lt;/p&gt;

&lt;p&gt;For a later build I would rather package more of those assets locally. For this first public version, documenting the limitation was more honest than pretending it did not exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not expose a checkout that cannot finish
&lt;/h2&gt;

&lt;p&gt;The larger project has more areas and systems than the itch.io demo. The uploaded build only includes the first three universes: the engine core, a low-gravity area, and a high-entropy dystopian area.&lt;/p&gt;

&lt;p&gt;I kept later content out of the playable path and did not leave an incomplete purchase flow inside the itch.io version. A button that looks like a real checkout but cannot complete is worse than a clear “demo ends here” boundary.&lt;/p&gt;

&lt;p&gt;This was less about payment implementation and more about player trust. The public build should only promise what it can deliver in that session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Play more than the opening minute
&lt;/h2&gt;

&lt;p&gt;A loading screen and ten seconds of movement can make almost any prototype look functional. I played several complete sessions and kept going after the first successful interaction.&lt;/p&gt;

&lt;p&gt;I wanted to know whether switching universes remained understandable, whether the controls stayed responsive, and whether the game still had something meaningful to investigate after the initial visual impression wore off.&lt;/p&gt;

&lt;p&gt;The result is still a demo, not a finished commercial game. Publishing it was useful precisely because the boundary is now visible. I can ask for feedback on the actual browser build instead of showing screenshots and guessing how it feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be specific about generated content
&lt;/h2&gt;

&lt;p&gt;Some of the project's content was produced with generative AI tools, then assembled, edited, and tested as an interactive game. I included that disclosure on the itch.io page.&lt;/p&gt;

&lt;p&gt;For me, “AI-generated” is not a substitute for testing. The useful question is still whether a player can load the page, understand the controls, move through the available spaces, refresh, and continue without hidden state breaking the experience.&lt;/p&gt;

&lt;p&gt;If you want to try the current browser build and tell me where it breaks, it is here: &lt;a href="https://zzzu2221.itch.io/entropy-the-critical-loop?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=entropy_itchio_20260818" rel="noopener noreferrer"&gt;ENTROPY: The Critical Loop on itch.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am especially interested in iframe focus problems, loading failures, and anything that behaves differently after a refresh.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why Restart Bugs Reveal Hidden Game State</title>
      <dc:creator>zzzu2221</dc:creator>
      <pubDate>Sun, 16 Aug 2026 07:02:49 +0000</pubDate>
      <link>https://dev.to/zzzu2221/why-restart-bugs-reveal-hidden-game-state-2njc</link>
      <guid>https://dev.to/zzzu2221/why-restart-bugs-reveal-hidden-game-state-2njc</guid>
      <description>&lt;p&gt;A game can look correct until the player presses Restart. That button is one of the fastest ways to expose hidden state.&lt;/p&gt;

&lt;p&gt;In a small browser game, I treat restart as a full lifecycle test rather than a visual reset.&lt;/p&gt;

&lt;h2&gt;
  
  
  What restart should clear
&lt;/h2&gt;

&lt;p&gt;A restart should create a fresh run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;player position and health;&lt;/li&gt;
&lt;li&gt;score and collected items;&lt;/li&gt;
&lt;li&gt;enemy timers and active projectiles;&lt;/li&gt;
&lt;li&gt;win and lose flags;&lt;/li&gt;
&lt;li&gt;keyboard and pointer listeners;&lt;/li&gt;
&lt;li&gt;pending animation and timeout callbacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resetting only the score is not enough if an old enemy loop is still running.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple failure pattern
&lt;/h2&gt;

&lt;p&gt;A common implementation keeps game objects in module-level variables and starts a new animation loop on every restart. The first run works. After two restarts, movement feels faster, collisions fire twice, and the result becomes inconsistent.&lt;/p&gt;

&lt;p&gt;The bug is not in the button label. It is in ownership of the running state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the lifecycle explicit
&lt;/h2&gt;

&lt;p&gt;I prefer a small state machine: loading → playing → won/lost → restarting.&lt;/p&gt;

&lt;p&gt;The transition into restarting should stop the current loop, remove listeners, cancel timers, and create a new run context. The new context then owns its entities and callbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the failure path first
&lt;/h2&gt;

&lt;p&gt;I run this sequence several times:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start a fresh game.&lt;/li&gt;
&lt;li&gt;Lose intentionally.&lt;/li&gt;
&lt;li&gt;Restart.&lt;/li&gt;
&lt;li&gt;Win or lose again.&lt;/li&gt;
&lt;li&gt;Refresh the page and repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the second run differs from the first without a design reason, I inspect duplicated listeners, stale references, and callbacks that still point at the previous run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical lesson
&lt;/h2&gt;

&lt;p&gt;Restart is more than a convenience button. It is a compact test of whether the game has a coherent lifecycle. When restart is reliable, win, lose, refresh, and replay are usually easier to reason about as well.&lt;/p&gt;

</description>
      <category>gamedev</category>
    </item>
    <item>
      <title>A Game Wallet Is More Than a Number: Handling Retries and Concurrency</title>
      <dc:creator>zzzu2221</dc:creator>
      <pubDate>Sun, 16 Aug 2026 06:45:33 +0000</pubDate>
      <link>https://dev.to/zzzu2221/a-game-wallet-is-more-than-a-number-handling-retries-and-concurrency-2al3</link>
      <guid>https://dev.to/zzzu2221/a-game-wallet-is-more-than-a-number-handling-retries-and-concurrency-2al3</guid>
      <description>&lt;p&gt;A game wallet often starts as a single balance field. That is fine for a prototype, but payment retries and unreliable networks quickly make the number hard to trust.&lt;/p&gt;

&lt;p&gt;A player can tap “buy,” lose the connection, and try again. A store callback can arrive more than once. Two devices can spend the same account at nearly the same time.&lt;/p&gt;

&lt;p&gt;The fix is to treat the balance as a cached view of a ledger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record every change
&lt;/h2&gt;

&lt;p&gt;Instead of silently changing a balance, record events such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a verified payment granting virtual currency;&lt;/li&gt;
&lt;li&gt;an item purchase spending currency;&lt;/li&gt;
&lt;li&gt;a refund creating a compensating entry;&lt;/li&gt;
&lt;li&gt;an administrative adjustment with an explicit reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current balance remains useful for fast reads, but the ledger explains where it came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make payment delivery idempotent
&lt;/h2&gt;

&lt;p&gt;A payment callback is a message that may be retried. I use an idempotency key derived from the provider and transaction ID, then enforce uniqueness in the database.&lt;/p&gt;

&lt;p&gt;The delivery flow is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify the external transaction.&lt;/li&gt;
&lt;li&gt;Record the payment.&lt;/li&gt;
&lt;li&gt;Grant currency with the unique key.&lt;/li&gt;
&lt;li&gt;Mark delivery complete.&lt;/li&gt;
&lt;li&gt;Return the original result for later retries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prevents a temporary network problem from becoming a double grant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protect spending too
&lt;/h2&gt;

&lt;p&gt;Client-side balance checks are useful for interface feedback, but they cannot protect an account. The server should lock the wallet row, check the available amount, write the ledger entry, and update the cached balance in one transaction.&lt;/p&gt;

&lt;p&gt;The same request key should return the original purchase result instead of charging twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep payment, wallet, and item orders separate
&lt;/h2&gt;

&lt;p&gt;A real-money payment, a virtual-currency movement, and item delivery are connected but different events:&lt;/p&gt;

&lt;p&gt;payment order → wallet grant → item order → wallet spend&lt;/p&gt;

&lt;p&gt;That separation makes refunds and reconciliation much easier. When something goes wrong, support can identify which step is missing instead of guessing from one mutable number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests that expose the real failures
&lt;/h2&gt;

&lt;p&gt;Before building a large shop UI, test repeated callbacks, crashes between payment and delivery, concurrent spends, timeout retries, failed item delivery, and refunds after currency has been spent.&lt;/p&gt;

&lt;p&gt;The important shift is simple: a wallet is not just a number. It is a history of decisions. Making those decisions explicit and idempotent keeps payment failures explainable.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
  </channel>
</rss>
