<?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>A practical first-minute playtest for browser games</title>
      <dc:creator>NextGenRunGames</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:42:14 +0000</pubDate>
      <link>https://dev.to/nextgenrungames/a-practical-first-minute-playtest-for-browser-games-7hn</link>
      <guid>https://dev.to/nextgenrungames/a-practical-first-minute-playtest-for-browser-games-7hn</guid>
      <description>&lt;p&gt;A browser game can be easy to open and still be hard to understand. In a short playtest, the first minute can reveal whether players see the main action, understand its result, and know what they can try next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show the action before the instructions
&lt;/h2&gt;

&lt;p&gt;Start a new player at the normal opening state. Let them play without coaching. Note when they make their first meaningful input, when they recognize what happened, and when they reach a decision that changes the next attempt.&lt;/p&gt;

&lt;p&gt;The right sequence depends on the game. In Flingkeep, a player grabs an attacker, chooses an angle, and releases it toward the siege. In Spirestorm, a dive and rebound lead into a choice to bank a haul or climb farther. In Line &amp;amp; Lode, the player casts a claw, tunes its pull, and tries to free ore before the chain snaps. Each loop asks the preview to make cause and effect visible quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask about understanding, not approval
&lt;/h2&gt;

&lt;p&gt;After the play session, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What did you think the goal was?&lt;/li&gt;
&lt;li&gt;What told you that an action had worked or failed?&lt;/li&gt;
&lt;li&gt;What did you want to try next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions help distinguish a confusing goal from feedback the player did not notice. Ask after play rather than explaining during the attempt; otherwise the test measures your instructions as much as the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change one thing and observe again
&lt;/h2&gt;

&lt;p&gt;Choose the earliest moment that caused confusion. Adjust one part of the experience—such as the timing of a cue, the contrast of a result, or the transition into the next choice—then repeat the same short test. Changing several systems at once makes it harder to learn which adjustment helped.&lt;/p&gt;

&lt;p&gt;A browser preview does not need to contain a full campaign. It needs to communicate one honest reason to continue and make the current playable state clear. Our current projects and builds are listed at &lt;a href="https://www.nextgenrungames.com/" rel="noopener noreferrer"&gt;NextGenRunGames&lt;/a&gt;, with each project page describing its status.&lt;/p&gt;

&lt;p&gt;Some projects use AI-assisted programming and text plus pre-generated AI-assisted artwork. No AI content is generated during gameplay; individual game pages list the relevant details.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>webdev</category>
      <category>testing</category>
    </item>
    <item>
      <title>Build Steam wishlist links with URLSearchParams: a four-game campaign example</title>
      <dc:creator>NextGenRunGames</dc:creator>
      <pubDate>Sat, 10 Oct 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/nextgenrungames/build-steam-wishlist-links-with-urlsearchparams-a-four-game-campaign-example-5298</link>
      <guid>https://dev.to/nextgenrungames/build-steam-wishlist-links-with-urlsearchparams-a-four-game-campaign-example-5298</guid>
      <description>&lt;p&gt;A Steam wishlist campaign needs links that tell you which channel a visitor came from. Here is the small helper we use for our October studio posts. It generates one URL per game and channel, so the copy and the link use the same campaign identifier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the inputs separate
&lt;/h2&gt;

&lt;p&gt;Store the app ID with the game, and keep the channel name separate from the campaign name. Avoid rebuilding a query string by hand for every post.&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;const&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;steam_wishlist_oct2026_batch1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;wishlistUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gameId&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://store.steampowered.com/app/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;utm_source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;utm_medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;social&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;utm_campaign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;utm_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;wishlistUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5308920&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="s1"&gt;devto&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="s1"&gt;spirestorm&lt;/span&gt;&lt;span class="dl"&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;URLSearchParams&lt;/code&gt; handles query encoding. The app ID chooses the destination; the source distinguishes DEV from another channel; the campaign groups the batch; and the content value distinguishes the game and copy version. Those roles are conventions in this example, so choose names that your team can keep consistent.&lt;/p&gt;

&lt;p&gt;If you change the caption later, you can increment the content version. Keep the campaign value stable while comparing posts within the same batch. Do not put a reader's name, email address or other personal data in these URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Steam can measure
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://partner.steamgames.com/doc/marketing/utm_analytics" rel="noopener noreferrer"&gt;Steam's UTM Analytics documentation&lt;/a&gt; describes the supported tags and the distinction between visits and eligible tracked visits. Conversion tracking depends on a visitor being logged in to Steam and is subject to privacy limits. A tagged visit is not proof of a wishlist or a sale. We have no campaign results to report yet.&lt;/p&gt;

&lt;p&gt;Our practical workflow is to save the actual scheduled post URL alongside its Steam destination, then check publication before recording any engagement. A prepared caption is a draft; a platform-confirmed future post is a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four real destinations
&lt;/h2&gt;

&lt;p&gt;The helper is being used for these public Coming Soon pages. Each link below uses &lt;code&gt;devto&lt;/code&gt; as its source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://store.steampowered.com/app/5308920/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=steam_wishlist_oct2026_batch1&amp;amp;utm_content=spirestorm_v1" rel="noopener noreferrer"&gt;Spirestorm&lt;/a&gt;: Your next foothold is an enemy. Dive into a pack, land a strike and rebound higher in Spirestorm. Bank your gold, or risk another climb.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://store.steampowered.com/app/4982520/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=steam_wishlist_oct2026_batch1&amp;amp;utm_content=understar_v1" rel="noopener noreferrer"&gt;UNDERSTAR: Dig Below, Rise Above&lt;/a&gt;: Every shaft you dig becomes part of the next expedition. Explore a persistent mine, fly through caverns and search for lost stars in UNDERSTAR.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://store.steampowered.com/app/5283550/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=steam_wishlist_oct2026_batch1&amp;amp;utm_content=debt-daylight_v1" rel="noopener noreferrer"&gt;Debt &amp;amp; Daylight&lt;/a&gt;: Six midnight bills. One physical coin pusher.  In Debt &amp;amp; Daylight, aim each coin drop and choose workshop upgrades while the clock runs down. Can your machine cover the next payment?&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://store.steampowered.com/app/5341380/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=steam_wishlist_oct2026_batch1&amp;amp;utm_content=line-lode_v1" rel="noopener noreferrer"&gt;Line &amp;amp; Lode&lt;/a&gt;: How much strain can your next haul take?  In Line &amp;amp; Lode, cast a claw, adjust reel force and bring ore home before the chain gives out. Upgrade your rig for the next claim.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the full-game Coming Soon pages. Demo availability is separate. Flingkeep's main Steam page is still in review, so it is excluded from these wishlist links.&lt;/p&gt;

&lt;p&gt;This article was written by our AI assistant from the current studio campaign and Steam documentation. The games' individual Steam pages contain their content and AI-use disclosures.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>webdev</category>
    </item>
    <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>
