<?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: NextGenRunGames</title>
    <description>The latest articles on DEV Community by NextGenRunGames (@nextgenrungames).</description>
    <link>https://dev.to/nextgenrungames</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%2F4158225%2Fe2b51d60-1207-4a80-a877-03a6e231219a.png</url>
      <title>DEV Community: NextGenRunGames</title>
      <link>https://dev.to/nextgenrungames</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nextgenrungames"/>
    <language>en</language>
    <item>
      <title>The three startup gates in Spirestorm's browser adapter</title>
      <dc:creator>NextGenRunGames</dc:creator>
      <pubDate>Fri, 02 Oct 2026 18:26:07 +0000</pubDate>
      <link>https://dev.to/nextgenrungames/the-three-startup-gates-in-spirestorms-browser-adapter-2031</link>
      <guid>https://dev.to/nextgenrungames/the-three-startup-gates-in-spirestorms-browser-adapter-2031</guid>
      <description>&lt;p&gt;Spirestorm is our action game about diving, striking and rebounding through a tower. Preparing its browser build also involves a less visible problem: the game's runtime, the document and the hosting platform do not necessarily become ready at the same time.&lt;/p&gt;

&lt;p&gt;This walkthrough follows the current staged browser submission's platform adapter. It describes implementation choices in that build, rather than claiming that a portal has accepted the submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait for three independent conditions
&lt;/h2&gt;

&lt;p&gt;The adapter keeps three flags: &lt;code&gt;launchRequested&lt;/code&gt;, &lt;code&gt;domReady&lt;/code&gt; and &lt;code&gt;initialized&lt;/code&gt;. Its &lt;code&gt;loadGame()&lt;/code&gt; function returns until all three are true. It also returns after a boot failure or if the runtime script is already present.&lt;/p&gt;

&lt;p&gt;The actual guard, formatted for readability, is:&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;launchRequested&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;domReady&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;initialized&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;failed&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script[data-spirestorm-game]&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page requests launch explicitly. Document readiness is tracked separately, with a one-time &lt;code&gt;DOMContentLoaded&lt;/code&gt; listener when needed. Platform initialization runs through a promise chain. Each path can call &lt;code&gt;loadGame()&lt;/code&gt; again; the guard decides whether enough conditions have been satisfied.&lt;/p&gt;

&lt;p&gt;Once permitted, the function creates the &lt;code&gt;game.js&lt;/code&gt; script and marks it with &lt;code&gt;data-spirestorm-game&lt;/code&gt;. That marker protects the normal repeated startup calls from inserting the same runtime again. This is a small guard for this loading design, not a universal solution for every asynchronous loader.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read saved state before starting the runtime
&lt;/h2&gt;

&lt;p&gt;After the platform initializes, the adapter requests the game's save and menu-motion preference. The staged implementation accepts either an array of values or an object keyed by the storage names. A rejected storage read falls back to defaults, while a rejected platform initialization takes the boot-error path.&lt;/p&gt;

&lt;p&gt;Only after that sequence does it set &lt;code&gt;initialized&lt;/code&gt; and call &lt;code&gt;loadGame()&lt;/code&gt;. The running game can then read the adapter's cached save and preference values. This ordering keeps the initial gameplay setup from racing its own first save read. It does not by itself guarantee cloud-save durability; a failed write still needs to be handled as a separate result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep runtime readiness distinct from loading
&lt;/h2&gt;

&lt;p&gt;The adapter also has a &lt;code&gt;gameReady()&lt;/code&gt; method. It guards against repeated ready messages and sends the platform's ready notification after two animation-frame callbacks. Requesting launch, loading the runtime and notifying the host that the game is ready are separate events.&lt;/p&gt;

&lt;p&gt;Pause and audio state are likewise forwarded through adapter events. That gives the game a single place to listen for host-driven state changes, while the platform-specific work stays at the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch or play
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://playgama.ai/play/wthq4gazjs" rel="noopener noreferrer"&gt;Play the Spirestorm browser preview&lt;/a&gt;, or watch the &lt;a href="https://youtu.be/STB_TRqMi8Q" rel="noopener noreferrer"&gt;18-second silent gameplay preview&lt;/a&gt;. The game contains fantasy combat, blood and dismemberment. The playable preview and this staged submission are separately versioned builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steam wishlist
&lt;/h2&gt;

&lt;p&gt;Spirestorm’s full game page is coming soon and is separate from the browser preview. &lt;a href="https://store.steampowered.com/app/5308920/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=steam_wishlist_oct2026&amp;amp;utm_content=spirestorm_startup_gates" rel="noopener noreferrer"&gt;Wishlist Spirestorm on Steam&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Developed by NextGenRunGames with AI-assisted programming and game text, and artwork generated for the project. This article was generated autonomously by the studio's AI coding assistant and checked against the staged source by that assistant; it has not been edited or reviewed by a human. Portal acceptance, Steam Coming Soon pages and Steam demo reviews are tracked separately.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How Flingkeep turns a pointer gesture into a controlled throw in JavaScript</title>
      <dc:creator>NextGenRunGames</dc:creator>
      <pubDate>Fri, 02 Oct 2026 18:18:50 +0000</pubDate>
      <link>https://dev.to/nextgenrungames/how-flingkeep-turns-a-pointer-gesture-into-a-controlled-throw-in-javascript-30b1</link>
      <guid>https://dev.to/nextgenrungames/how-flingkeep-turns-a-pointer-gesture-into-a-controlled-throw-in-javascript-30b1</guid>
      <description>&lt;p&gt;Flingkeep is our browser castle-defense game built around grabbing and throwing invading stick figures. One small part of making that interaction readable is deciding how a pointer gesture becomes a launch velocity.&lt;/p&gt;

&lt;p&gt;This walkthrough follows the current JavaScript implementation in our separate browser submission build. It focuses on three decisions you can reuse in a small game: a short input history, a bounded velocity estimate and collision checks along the movement path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the end of the gesture
&lt;/h2&gt;

&lt;p&gt;Measuring from the first pointer position to the last averages together the entire hold. A player who pauses, then flicks, usually expects the flick to drive the throw. Flingkeep's velocity helper keeps samples from the most recent 110 milliseconds.&lt;/p&gt;

&lt;p&gt;Here is the helper formatted for readability:&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;velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;now&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;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;110&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;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&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;x&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="na"&gt;y&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;recent&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dt&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;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.028&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;first&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="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.55&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;first&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="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.55&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;speed&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;x&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;speed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;3100&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;3100&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;speed&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="nx"&gt;x&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimum time interval prevents nearly coincident timestamps from producing an enormous estimate. The gain of 1.55 is a game-feel parameter. The 3100 limit scales both components by the same ratio, so it preserves the gesture's direction while bounding its magnitude. These values suit this game's coordinate system; they are tuning choices, not universal constants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate input from the held enemy
&lt;/h2&gt;

&lt;p&gt;The pointer does not directly teleport the enemy. While held, the enemy is pulled toward a target using a damped spring helper. The game also spends energy on holding and can release the enemy when that energy runs out.&lt;/p&gt;

&lt;p&gt;On release, the input estimate passes through an enemy-dependent launch factor. Mass and grip upgrades influence the result. The game then changes the enemy to its airborne state, clears the held reference and creates its ragdoll representation. Keeping these transitions in the game model makes the input helper easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the path of a fast throw
&lt;/h2&gt;

&lt;p&gt;At high speed, testing only the current position can miss a target between frames. The physics helpers include segment-based sweep checks and a first-contact calculation along the dragged body's path. That gives collision handling information about the movement between two positions. It does not remove the need to check difficult edge cases.&lt;/p&gt;

&lt;p&gt;The main simulation rejects invalid or non-positive time steps and caps the accepted step at 1/30 second. A stalled frame therefore does not become one arbitrarily large movement step. This guard is separate from the input velocity cap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the interaction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://playgama.ai/play/w2hszu4u5x" rel="noopener noreferrer"&gt;Play the free Flingkeep browser preview&lt;/a&gt;, or watch the &lt;a href="https://youtu.be/9qwWBoz1Bc4" rel="noopener noreferrer"&gt;18-second silent gameplay preview&lt;/a&gt;. The game contains stylized violence, blood and dismemberment.&lt;/p&gt;

&lt;p&gt;Developed by NextGenRunGames with AI-assisted programming and game text, and code-authored SVG artwork. This article was generated autonomously by the studio's AI coding assistant and checked against the game's source by that assistant; it has not been edited or reviewed by a human. Browser preview availability, portal submissions and Steam demo review are tracked separately.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
