<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[CGT Rocket X]]></title><description><![CDATA[CGT Rocket X]]></description><link>https://cgtrocketx.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 28 Oct 2026 09:21:06 GMT</lastBuildDate><atom:link href="https://cgtrocketx.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Before you blame the WASM heap: auditing Godot 4 web "Sample" audio for long-session memory creep]]></title><description><![CDATA[A rising memory graph is a reason to investigate, not a diagnosis. For CGT Rocket X Racer, the question was whether repeated audio activity would leave…]]></description><link>https://cgtrocketx.hashnode.dev/before-you-blame-the-wasm-heap-auditing-godot-4-web-sample-audio-for-long-session-memory-creep</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/before-you-blame-the-wasm-heap-auditing-godot-4-web-sample-audio-for-long-session-memory-creep</guid><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Tue, 27 Oct 2026 00:18:31 GMT</pubDate><content:encoded><![CDATA[<h2>Debug diary: checking for long-session creep</h2>
<p>A rising memory graph is a reason to investigate, not a diagnosis. For CGT Rocket X Racer, the question was whether repeated audio activity would leave memory climbing during an extended web session. The WASM heap was a candidate to inspect, alongside the browser's audio activity and process memory. We had no basis for choosing a culprit before measuring.</p>
<p>Our audited build uses Godot 4.7.2 stable, the Compatibility renderer, and a single-threaded web export. The baseline is state A: the existing Sample default and pooled SFX players that change streams on each SFX call.</p>
<p>The diary's first result is deliberately uneventful: a 30-minute run did not show a memory slope pointing at Sample-audio churn. That makes this an audit and a decision checklist, rather than a fix announcement. The open reports made the pattern worth examining; they did not establish that our build reproduced their failures.</p>
<h2>What "Sample" actually means on web</h2>
<p>Sample uses the browser's Web Audio API instead of having Godot mix audio streams. The <a href="https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-audio-general-default-playback-type-web">web playback setting</a> defaults to Sample because the web platform is poorly suited to mixing outside that API, particularly for single-threaded exports. The documented benefit is lower latency, with reduced flexibility: AudioEffects are unsupported.</p>
<p>Stream is therefore a trade-off to investigate. The documentation warns that forcing it on web may introduce high latency and crackling, especially with multithreaded exports. That warning belongs beside any proposed switch, even when a particular issue reporter found Stream helpful.</p>
<p>A player's <a href="https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-property-playback-type">playback_type</a> can override the default; the same documentation says assigning its stream stops currently playing sounds on that player. These settings are Experimental, so their behavior should remain part of an upgrade review.</p>
<p>The Experimental <a href="https://docs.godotengine.org/en/stable/classes/class_audioserver.html#class-audioserver-method-register-stream-as-sample">register_stream_as_sample</a> method forces registration, and its documentation recommends doing this while loading assets to mask possible lag spikes, especially in single-threaded builds.</p>
<h2>What the official issues say (and don't)</h2>
<p><a href="https://github.com/godotengine/godot/issues/106098">Issue #106098</a> remains open. Its desktop Linux reproduction used Godot 4.4.1 with Firefox 137: Sample memory increased over time and with stream changes, while Stream stayed at baseline after repeated changes. That is a useful reason to inspect stream reassignment. It is not evidence that every pooled audio implementation leaks.</p>
<p><a href="https://github.com/godotengine/godot/issues/116750">Issue #116750</a> also remains open. Its Godot 4.5.1 report describes an iOS Safari page crash after repeated audio playback for 10–30 minutes. Switching to Stream avoided the crash for that reporter. However, the report explicitly calls its memory evidence inconclusive, and the reporter's lack of severe distortion may reflect simple audio. Neither observation settles our desktop build's behavior.</p>
<p>There is relevant repair history: <a href="https://github.com/godotengine/godot/issues/107390">issue #107390</a> is closed, and <a href="https://github.com/godotengine/godot/pull/107948">PR #107948</a> restored pooling of position-reporting audio worklets because WebKit did not delete finished processors. The #116750 reporter notes that this fix was already present in 4.5.1. The accurate summary is “partly fixed, still open.” A specific repaired leak and continuing reports can coexist; the history does not identify the cause of an unmeasured symptom in our game.</p>
<h2>Audit our own pattern</h2>
<p>The packed-project audit found no audio settings in the exported project settings. No shipped script or scene sets <code>playback_type</code>, and nothing calls <code>AudioServer.register_stream_as_sample</code>. Our players therefore follow the web Sample default, with clips registered lazily on first play.</p>
<p>The feedback script creates a pool of 6 <code>AudioStreamPlayer</code> nodes after the first real user input. It rotates through them, assigns the requested clip to the next player, and calls <code>play()</code>. Reusing those players does not mean their streams stay fixed.</p>
<p>The verified SFX list contains 17 WAV clip names, including coin, crash, jump, and boost sounds. Coin pickups randomize pitch. Background music has separate players, and the page shell already contains a user-gesture shim that resumes the browser's AudioContext. Those details describe the implementation; they do not prove retention or explain a memory graph by themselves.</p>
<p>My checklist starts with concrete questions: do pooled players receive a different stream on each hit? How many distinct clips are involved? What session length should the test represent? Audit those answers before treating a similar issue title as a diagnosis.</p>
<h2>The experiment matrix we're running: decision data, not opinions</h2>
<p>Only state A has been measured. The run used headless Google Chrome 156.0.8078.47 on a Linux box, with SwiftShader software GL and no real GPU. Scripted random input kept the game in its tutorial/practice flow. This was not skilled, normal play.</p>
<p>The measured target was the public formal deployment.</p>
<p>Sampling every 60 seconds separated the game renderer's process RSS, the software GL process, JavaScript heap usage, and WASM linear memory. Browser-side wrappers also counted Web Audio buffer sources and declared buffer allocations. Navigation and crash checks watched for page failure.</p>
<p>These selected state-A samples retain the measurement file's units and values:</p>
<table>
<thead>
<tr>
<th align="right">Minute</th>
<th align="right">Renderer RSS (MB)</th>
<th align="right">Software GL process RSS (MB)</th>
<th align="right">JS heap used (MB)</th>
<th align="right">WASM memory (MB)</th>
</tr>
</thead>
<tbody><tr>
<td align="right">0</td>
<td align="right">652</td>
<td align="right">406</td>
<td align="right">12.4</td>
<td align="right">177.8</td>
</tr>
<tr>
<td align="right">10</td>
<td align="right">656</td>
<td align="right">484</td>
<td align="right">14.1</td>
<td align="right">177.8</td>
</tr>
<tr>
<td align="right">20</td>
<td align="right">671</td>
<td align="right">485</td>
<td align="right">17.1</td>
<td align="right">177.8</td>
</tr>
<tr>
<td align="right">30</td>
<td align="right">668</td>
<td align="right">485</td>
<td align="right">15.5</td>
<td align="right">177.8</td>
</tr>
</tbody></table>
<p>WASM memory never grew. Renderer RSS stayed flat within noise, while the software GL process plateaued after 10 minutes. The JavaScript heap had a small upward drift that this run could not separate from garbage-collection timing. No reload or crash occurred.</p>
<p>Audio was active: the page created 3,171 Web Audio buffer sources during the session. Declared AudioBuffer allocations accumulated to 748.5 MB, but that is allocation churn, not retained memory. With renderer RSS flat within noise, the measurement supports buffers being reclaimed in this Chrome run. These page-wide counters include SFX and music; they are not a QA counter of SFX <code>play()</code> calls.</p>
<p>Variant B is a next step, not a result: retain Sample, dedicate a player to each clip without shared-player stream reassignment, and pre-register during loading or menus. This follows the documented registration option and the reassignment concern in the open report. It is a hypothesis to test, not a demonstrated remedy.</p>
<p>Variant C is also a next step, not a result: select Stream through <code>default_playback_type.web</code>, or through an individual player's override, while accepting the documented latency and crackle warning. Neither alternative was built or measured here.</p>
<h2>Decision checklist</h2>
<ul>
<li><strong>Keep the baseline tied to its workload.</strong> A short intended session and a need for low latency can make Sample worth retaining when AudioEffects are unnecessary. Test the session you actually intend to support before turning that preference into a release decision.</li>
<li><strong>Inspect reassignment before redesigning playback.</strong> Per-clip players and registration during loading are candidates for the next comparison. The issue report motivates that comparison; it does not establish a memory benefit for our code.</li>
<li><strong>Require evidence for a Stream switch.</strong> If your own test shows a persistent memory slope, investigate it and compare the alternative. Listen for the latency and crackling the documentation warns about before choosing it.</li>
<li><strong>Repeat the decision after engine upgrades.</strong> Experimental settings and open reports make an old result worth revisiting. Preserve the browser, workload, and measurement method so the next comparison answers a recognizable question.</li>
</ul>
<h2>What we're shipping + honest limits</h2>
<p>No variant has been chosen yet, because only state A was measured. There is no measured before-and-after improvement to announce. The current result supports continuing the investigation without blaming the WASM heap or declaring Sample responsible for creep in this run.</p>
<p>The limits are substantial: one run, one machine, desktop Chrome only, headless execution, software rendering, and scripted tutorial input. Absolute RSS values are not comparable to ordinary desktop Task Manager readings. There was no real audio device or listener. This says nothing about our build on Safari, Firefox, or phones.</p>
<p>The next useful evidence is a comparable run on the browsers of concern and then the proposed variants. The linked issue threads provide context for contributors; our Chrome baseline cannot close those reports.</p>
<h2>Close + CTA</h2>
<p>Have you seen Sample-mode memory climb on your web build? Which browser, and after how many minutes?</p>
<p>Play free in your PC browser (no install), Android on Google Play, iOS coming soon: cgt.monster/x/hash8</p>
<p>Fan-made · Not affiliated</p>
]]></content:encoded></item><item><title><![CDATA[Before more GDScript: VRAM-compressed atlases that fixed our Godot 4 web stutter]]></title><description><![CDATA[Fan-made · Desktop-only · Not affiliated with any car maker In the desktop browser build of CGT Rocket X Racer, a scene could be running comfortably until…]]></description><link>https://cgtrocketx.hashnode.dev/before-more-gdscript-vram-compressed-atlases-godot-4-web-stutter</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/before-more-gdscript-vram-compressed-atlases-godot-4-web-stutter</guid><category><![CDATA[Godot]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[webdev]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Sun, 25 Oct 2026 13:56:50 GMT</pubDate><content:encoded><![CDATA[<p>Fan-made · Desktop-only · Not affiliated with any car maker</p>
<h2>1. The hitch that did not need more GDScript</h2>
<p>In the desktop browser build of CGT Rocket X Racer, a scene could be running comfortably until a large atlas entered the picture. The first appearance interrupted the flow. Seeing the same content again was a different experience: the interruption was tied to first use, rather than every frame of gameplay.</p>
<p>That pattern invites a familiar response. Add another <code>preload</code>. Move resource loading behind a loading screen. Try a background request through <code>ResourceLoader</code>. Keep another reference alive so the resource stays cached.</p>
<p>Those tools have legitimate jobs. But our useful change happened earlier in the pipeline: how the artwork was imported, how much texture data each atlas represented, and which compressed formats the web export contained.</p>
<p>Switching the relevant atlases to VRAM compression and breaking up oversized sheets addressed the visible first-use stutter on the route we were checking. That is a qualitative result, not a benchmark claim about every browser or every Godot game.</p>
<p>The lesson was specific: before adding another layer of GDScript around a first-use hitch, inspect the resource that rendering must make usable. A perfectly reasonable loading script can still hand the renderer an unreasonable texture.</p>
<h2>2. A loaded resource is not a promise of GPU readiness</h2>
<p>It helps to separate three milestones: the bytes are available, the engine has loaded the resource, and the rendering path can use it without introducing a noticeable pause.</p>
<p>These milestones are related, but they are not interchangeable. <code>preload</code> establishes a resource dependency when a script loads. A threaded loading request can move supported resource-loading work away from the caller. Neither API is a general guarantee that the first visible draw will be cheap.</p>
<p>Saying that loading handles “only I/O” would be too narrow. Resource loading can include decoding and other preparation, and some rendering work may happen during that process. The important boundary is that completion of a resource request does not establish a universal first-frame latency guarantee.</p>
<p>Texture allocation, transfer, driver work, and shader preparation can still matter around first use. Their exact timing depends on the engine version, rendering path, browser, and driver. A first-use pause is therefore a clue to investigate, not proof that one particular upload call caused it.</p>
<p>Threading also needs precision. Godot supports single-threaded web exports; multithreaded exports require the appropriate <code>SharedArrayBuffer</code> and cross-origin-isolation setup. Do not assume a desktop background-loading strategy provides equivalent concurrency in a single-threaded browser build. The supported deployment requirements are described in Godot’s <a href="https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html">web export documentation</a>.</p>
<p>Nor is “RenderingServer is always single-threaded” a useful blanket explanation. The practical constraint is that work required by rendering must eventually be serviced through the active rendering configuration. Adding a worker does not make the texture smaller or remove that work.</p>
<p>For CGT, that changed the debugging question from “Where can we add another load?” to “What are we asking the browser to prepare when this atlas becomes relevant?”</p>
<h2>3. The three changes in our atlas pipeline</h2>
<h3>Import the critical atlases as VRAM Compressed</h3>
<p>We targeted the large UI, vehicle, and level atlases associated with first-use interruptions. In the Import dock, the relevant setting is <strong>Compress &gt; Mode &gt; VRAM Compressed</strong>. Apply the setting to the source texture and reimport it; changing a resource reference alone does not change the underlying image’s import format.</p>
<p>Lossless compression can keep stored assets compact while leaving a much larger uncompressed texture at runtime. VRAM compression uses GPU-oriented formats that reduce texture memory requirements, with quality tradeoffs. Godot’s <a href="https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_images.html">image import documentation</a> explains the distinction and format restrictions.</p>
<p>This was a selective change, not an instruction to compress every image. A large piece of vehicle artwork and a tiny, sharp interface symbol have different tolerances. Check transparent silhouettes, gradients, small labels, and edges against contrasting backgrounds. Keep exceptions where compression damages readability.</p>
<p>A useful review question is: at the actual gameplay scale, does this texture retain the information the player needs? Inspecting only a zoomed-in source image can miss both acceptable tradeoffs and visible in-game defects.</p>
<h3>Split oversized sheets around their actual use</h3>
<p>Our working target was atlas pages no larger than <strong>2048 × 2048</strong>, with <strong>4096 × 4096</strong> as the ceiling for this web asset budget. These are project constraints, not universal WebGL limits or a guarantee that every texture below the ceiling will be inexpensive.</p>
<p>The arithmetic explains why dimensions deserve attention. An uncompressed 4096 × 4096 RGBA8 base level contains 64 MiB of pixel data: width multiplied by height multiplied by four bytes. That is an illustrative calculation, not a measurement of CGT’s memory usage. Mipmaps, staging allocations, and other resources can add more.</p>
<p>A sprite occupying a small region does not turn the backing atlas into a small texture. If several unrelated screens share one enormous sheet, showing a small part can still involve preparing the backing resource.</p>
<p>Split by what needs to be available together: persistent interface art, a vehicle presentation, or a particular level’s artwork. Preserve useful grouping within those pages. Creating hundreds of tiny textures can introduce its own texture-switching and management costs.</p>
<p>Also inspect the dependencies after splitting. If the initial scene still references every new page, the export may prepare them together anyway. Smaller files are only part of the change; the resource relationships must make the smaller units useful.</p>
<h3>Export formats that match the browser rendering path</h3>
<p>In the Web export preset, we enabled <strong>VRAM Texture Compression &gt; For Desktop</strong> and <strong>For Mobile</strong>. These options package format variants; the second checkbox does not make this a mobile-supported product. Including both increases export size while broadening format compatibility, as the web export documentation notes.</p>
<p>We also kept <strong>Compress &gt; High Quality</strong> disabled for these Compatibility textures. Godot documents high-quality VRAM compression as supported by Forward+ and Mobile renderers, rather than Compatibility. An attractive editor result is not enough evidence that an imported format is suitable for the web renderer.</p>
<p>Basis Universal is another option, with runtime transcoding costs that need testing. We did not make it the default answer to this hitch. The first experiment was deliberately narrower: suitable VRAM compression, smaller atlas pages, and a compatible export.</p>
<p>After changing import settings, let reimport finish and create a fresh web export. Test those generated files through the same kind of hosting setup the build will use. An editor play session cannot validate the exported texture variants.</p>
<h2>4. Validate the first encounter, not just the average frame rate</h2>
<p>The meaningful before-and-after question was whether the same first encounter still interrupted play. A steady average frame rate can hide a single pause at exactly the wrong moment.</p>
<p>Our qualitative result was that the previously troublesome route no longer showed the same visible first-use hitch after the atlas changes. That does not establish a universal speedup, and it does not mean loading work disappeared. Work absorbed during a loading screen should be reported separately from work eliminated or reduced.</p>
<p>A repeatable check can stay small:</p>
<ol>
<li>Keep a baseline export and an atlas-adjusted export. Hold gameplay code and the test route constant.</li>
<li>Start each run from a fresh page session. Record whether the browser cache is warm or cold.</li>
<li>Enter the scene and trigger the first appearance of the relevant artwork.</li>
<li>Repeat the encounter within that session to distinguish first-use behavior from recurring cost.</li>
<li>Compare loading behavior, visual quality, and the first playable transition on the desktop browser configurations you intend to support.</li>
</ol>
<p>Use the browser’s Performance panel to capture the transition. Correlate the long frame with the resource’s first use, and inspect rendering or WebGL activity where the tooling exposes it. If the pause aligns with texture preparation rather than recurring gameplay work, that supports the atlas hypothesis.</p>
<p>A large WebAssembly task alone does not identify texture upload as the cause. Browser traces may not expose enough detail to separate allocation, transfer, shader work, and synchronization. Controlled changes to one atlas provide useful additional evidence. If compressing or splitting it changes the symptom while gameplay remains constant, the resource deserves further attention.</p>
<p>Complete the regression check with screenshots and gameplay inspection. Look for damaged alpha edges, atlas bleeding, unreadable interface details, and missing textures. Record export size separately: a larger download containing multiple compressed variants can still have a better runtime texture footprint.</p>
<h2>5. What more scripting should not be asked to solve</h2>
<p>Avoid introducing synchronous <code>load()</code> calls for large textures in the active runner frame. Once the asset pipeline is reasonable, arrange loading so the player is not waiting on a new heavyweight dependency during an input-sensitive moment.</p>
<p>Warmup can still help with first-use shader or material preparation. It is a complement to resource work, not permission to retain an oversized atlas indefinitely. Moving a stall behind a loading screen changes when the player waits; reducing the underlying resource burden changes what the system must process.</p>
<p>The same distinction applies to caches. Holding references can prevent repeated resource loads, but keeping everything resident increases memory pressure. Cache decisions should follow the content’s lifetime, not a blanket rule that more retained resources must be faster.</p>
<p>There is also a debugging cost to adding scripts too early. Each extra cache, loading queue, and warmup scene adds state to inspect. Keep the first experiment easy to explain: change the problematic texture’s representation, export again, and replay the same first encounter.</p>
<p>This experience concerns a <strong>desktop-browser web build using Compatibility</strong>. Enabling additional texture-format variants does not establish phone-browser performance, touch usability, or mobile-web support. Those would require their own testing and product decisions.</p>
<h2>6. Fix the artwork contract before adding another loader</h2>
<p>For CGT Rocket X Racer, the useful fix was an art-pipeline change: VRAM-compressed atlases, bounded page dimensions, and export formats appropriate for the browser rendering path.</p>
<p>The principle is reusable without assuming every hitch has the same cause. Identify the first-use boundary, inspect the asset behind it, and compare a controlled export. Then decide whether additional loading or warmup code has a specific remaining job.</p>
<p>If you have investigated a similar Godot web or Compatibility hitch, which change moved the result: texture format, atlas dimensions, or something else in the rendering path?</p>
<p>Desktop build (no install): cgt.monster/x/hash7</p>
<p><strong>Update: now on Android.</strong> CGT Rocket X Racer, the fan-made flying-car factory run this post is about, is now free on Google Play: cgt.monster/x/gp-hash</p>
]]></content:encoded></item><item><title><![CDATA[One job at a time: sequencing a Godot web-runner outro]]></title><description><![CDATA[Most browser runners slam a score panel the instant the route ends. That is efficient. It is also three jobs fighting for the same second. In CGT Rocket X…]]></description><link>https://cgtrocketx.hashnode.dev/one-job-at-a-time-sequencing-a-godot-web-runner-outro</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/one-job-at-a-time-sequencing-a-godot-web-runner-outro</guid><category><![CDATA[Godot]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[webdev]]></category><category><![CDATA[indiedev]]></category><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Sun, 25 Oct 2026 12:07:56 GMT</pubDate><content:encoded><![CDATA[<p>Most browser runners slam a score panel the instant the route ends. That is efficient. It is also three jobs fighting for the same second. In CGT Rocket X Racer — a fan-made rocket-car runner for desktop browsers — the finish is a short sequence of single jobs: settle in the delivery hub, read a stock-style chart, then watch the garage pack leave. This post is about that sequencing choice, and why the leave beat has to read as delivery rather than a synced victory parade.</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
<h2>Settle: stop being a projectile before numbers speak</h2>
<p>The route alternates factory indoors with open air and finishes by flying into an end garage — a lit delivery hub, not a black loading screen. The car brakes and hovers on the pad. The camera orbits once. That orbit is not decoration for its own sake. Design intent: give the player one clean look at the vehicle they just piloted before any number claims attention.</p>
<p>If results slammed in during the last boost, the chart would compete with residual panic from presses and lane cuts. Settle separates “I survived the approach” from “here is what the run produced.” The keyboard contract for the run stays simple — A/D lane cuts, W or Space jump-flip, S ground roll, Shift boost — but none of that should still be firing in the player’s head when the chart opens.</p>
<h2>Lineup: the pack is a HUD rule made visible</h2>
<p>Cars-built equals the lowest of five part counters: body, chip, motor, thrust, energy. The HUD already painted the weakest bin red mid-run. The garage pack turns that rule into silhouettes. A short row means the red counter won. A fuller pack means collection stayed balanced.</p>
<p>The results screen still carries the precise total and a stock-style line that dips where damage landed. Design intent for pacing: chart first for information, pack second for proof. Numbers tell you how many. The lineup shows whether that number feels like a fleet or a thin escort. Players should not need a legend to understand why the row looks short — they already chased the red part for the last stretch of track.</p>
<p>For landscape framing we also cap how many cars the 3D pack tries to show at once, while the UI keeps the true count. A browser tab on Godot’s Compatibility renderer and the no-threads web template cannot treat an unbounded fleet as free. Readability and budget point the same way: a clear row beats a crowded vanishing point.</p>
<h2>Leave: staggered departure, not a parade</h2>
<p>After the chart, the scene moves to the front of the garage for the pack lift. Design intent is delivery: cars leave the factory and exit the frame. Hovering forever as thruster fireworks reads as celebration VFX, not as “you built these and they went out.”</p>
<p>Variation matters more than perfect sync. If every car rises on the same frame and holds the same heading, the eye files it under particle system. If peel timing and direction differ — some peel off, most keep going — the pack reads as a group of vehicles making a departure. That is the same thruster language players already trust mid-run: underbody hover jets that sell flight, side nozzle mist locked to body markers. Reusing those tells keeps the leave beat inside the game’s visual vocabulary instead of inventing a new outro style.</p>
<p>The garage decision still waits after the reward: ten skins behind CONFIRM, applied when the player commits. Settle → lineup → leave → choose skin is a sequence of single jobs. Runners retry fast; an outro that tries to be chart, fleet, and skin picker in one overlay loses all three.</p>
<h2>What this sequence protects upstream</h2>
<p>A pack lift only motivates when the run that fed it felt fair. Hazards are giant presses and sweeping indoor arms. Design intent upstream: distance-sampled hazard phase so boost does not scramble the stamp you meet at a landmark; indoor arms that keep a safe lane for the whole sweep; pickups that do not spawn inside a press stroke. When players believe a missing thrust part was their miss, a thin leave row is feedback. When they believe the stamp cheated, the same row is salt.</p>
<p>Mid-air commits stay the same window indoors and outdoors so the approach into the garage is muscle memory, not a biome surprise. The pack does not rewrite that contract. It only stages the collecting result after the route has earned trust.</p>
<h2>Checklist for other Godot 4 desktop-browser outros</h2>
<ul>
<li>Separate settle from score. Let the vehicle stop being a projectile before the chart speaks.</li>
<li>Tie the 3D pack to a rule players already saw on the HUD — ours is cars-built as the minimum of five parts.</li>
<li>Cap on-screen fleet size for landscape readability; keep the true total on the results UI.</li>
<li>Prefer staggered departure over synced rise; exit the frame instead of hovering as fireworks.</li>
<li>Reuse in-run thruster markers for the leave beat so Compatibility cost stays on motion, not new post-process.</li>
<li>Keep skin CONFIRM after the reward so the outro does not compete with customization.</li>
</ul>
<p>We are still tuning how long the orbit holds before the chart, and how much peel variation still reads as one pack instead of noise. Feedback from people shipping Godot 4 Compatibility + no-threads outros — especially fleet leaves that stay honest to a collecting rule — is welcome in the comments.</p>
<p>Desktop build (no install): cgt.monster</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
]]></content:encoded></item><item><title><![CDATA[Why we sample hazards by track distance, not wall-clock]]></title><description><![CDATA[A press that opens on a timer looks fine until someone holds boost. Suddenly the safe window that felt generous at cruise speed is already closed when the…]]></description><link>https://cgtrocketx.hashnode.dev/why-we-sample-hazards-by-track-distance-not-wall-clock</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/why-we-sample-hazards-by-track-distance-not-wall-clock</guid><category><![CDATA[Godot]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Thu, 22 Oct 2026 20:32:03 GMT</pubDate><content:encoded><![CDATA[<p>A press that opens on a timer looks fine until someone holds boost. Suddenly the safe window that felt generous at cruise speed is already closed when the car arrives. In CGT Rocket X Racer — a fan-made rocket-car runner for desktop browsers — we moved hazard motion off wall-clock time and onto distance along the track. This post is the deeper cut on why, and what that choice does to boost and flip fairness.</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
<h2>Wall-clock makes speed a cheat code</h2>
<p>Wall-clock sampling is the default instinct: every frame, advance the hazard by <code>delta</code>. At one speed that is fine. At three speeds it is a different game. Hold Shift and you cover more track per second, so you meet the stamp earlier in its cycle — or later, depending on when you entered the approach. The geometry did not change. Your arrival phase did. Players read that as randomness even when the code is deterministic.</p>
<p>Boost in this game has no cooldown. A tap holds for about 1.5 seconds and climbs toward 370; hold keeps that speed; release eases back over about one second. That curve is identical indoors and outdoors. If hazard phase still ran on wall-clock, every boost would invent a new press timing. The flip commit (W or Space) would then feel unreliable for a reason that has nothing to do with the flip itself.</p>
<h2>Distance sampling keeps the approach honest</h2>
<p>Distance sampling ties hazard phase to how far the car has traveled along the route, not how many real seconds have passed. The press cycle — down, dwell, up, reset — is parameterized by track meters (or an equivalent path parameter). Arrive at the same point on the track and you see the same phase, whether you coasted or held boost through the approach.</p>
<p>For sweeping indoor arms the same rule applies. Every indoor arm pattern must leave at least one safe lane for the whole sweep. Sampling the arm from distance means holding Shift does not rewrite which lane is open. The safe lane stays the safe lane; you just reach the decision point sooner. That is the fairness contract we want: boost changes when you decide, not what the geometry is when you decide.</p>
<h2>What this means for flip and boost together</h2>
<p>The mid-air flip is a fixed commit window in game time. At higher speed that same window covers more track distance. Under distance-sampled hazards, that interaction stays learnable:</p>
<ul>
<li>Boost shortens the time you have to read a telegraph, because you close distance faster.</li>
<li>Boost does not scramble the press phase you will meet at a given landmark.</li>
<li>A flip started at the same track marker clears or fails the same way relative to the stamp, modulo your own commit timing.</li>
</ul>
<p>If hazards were still on wall-clock, boost would do both: shorten read time <em>and</em> scramble phase. Players cannot separate “I flipped late” from “boost desynced the stamp.” Distance sampling removes the second failure mode so the first one can be practiced.</p>
<h2>Outdoor lighting is not a collider change</h2>
<p>The route alternates factory indoors with open air. Outdoors has less fog and a brighter sky. When an arm or press stretch appears near a hall exit, it still follows distance sampling and the safe-lane rule. The lighting change is a mood change, not a hitbox change. We audit those exit stretches the same way we audit deep-hall patterns: full cycle, several travel speeds, flag any frame where a legal lane is already sealed when a committed player would react.</p>
<h2>Browser constraints push proof offline</h2>
<p>The build uses Godot’s Compatibility renderer and the no-threads web template. Compatibility keeps desktop browser reach wide. No-threads keeps hosting as a plain static site without special cross-origin headers. Pattern audits run offline; verified distance curves ship with the build.</p>
<p>A short lane-change input buffer still exists for browser jitter. It forgives one dropped frame after a press; it does not invent a new hazard phase. Phase belongs to track distance. Input forgiveness belongs to the keyboard path.</p>
<h2>Practical takeaways for other web runners</h2>
<ul>
<li>If travel speed varies, sample mechanical hazards from path distance, not only from <code>delta</code>.</li>
<li>Keep boost and flip timing stable across biomes so “several speeds” in an audit means the same thing in play.</li>
<li>Require a permanent safe lane on sweeping indoor hazards; distance sampling is how that lane stays true under boost.</li>
<li>Prove cycles offline under Compatibility + no-threads budgets; ship timings, do not probe them live.</li>
<li>Treat lighting and fog as mood, never as a silent collider edit.</li>
</ul>
<p>We are still tightening dwell windows on presses that sit right after a boost-friendly straight. Feedback from people shipping Godot 4 browser runners on distance-based hazard curves is welcome.</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
]]></content:encoded></item><item><title><![CDATA[Making hazard windows feel earned in a Godot 4 browser runner]]></title><description><![CDATA[When a runner kills you, the first thought should be “I mistimed that,” not “that came out of nowhere.” CGT Rocket X Racer is a fan-made rocket-car runner…]]></description><link>https://cgtrocketx.hashnode.dev/making-hazard-windows-feel-earned-in-a-godot-4-browser-runner</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/making-hazard-windows-feel-earned-in-a-godot-4-browser-runner</guid><category><![CDATA[Godot]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Thu, 22 Oct 2026 15:26:42 GMT</pubDate><content:encoded><![CDATA[<p>When a runner kills you, the first thought should be “I mistimed that,” not “that came out of nowhere.” CGT Rocket X Racer is a fan-made rocket-car runner for desktop browsers, and most of our recent tuning work has gone into making giant presses and sweeping robot arms feel earned. This write-up walks through the design rules we settled on, how keyboard feel ties into those rules, and what the Godot web export forced us to leave out.</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
<h2>Start from the miss, not the art</h2>
<p>We stopped asking “does this press look cool?” and started asking “what does a fair miss look like?” A fair miss has three traits:</p>
<ol>
<li>The player can see the stroke starting early enough to choose a response.</li>
<li>At least one response still works at the current speed — flip, ground roll, or a lane cut.</li>
<li>The same pattern behaves the same way on the next attempt.</li>
</ol>
<p>If any of those fail, more particles will not fix it. That framing pushed us toward automation: every press pattern is walked through its full cycle (down, dwell, up, reset) at several travel speeds. The audit flags overlaps where a legal lane is already closed by the time a committed player could react.</p>
<h2>Arms that refuse to cover everything</h2>
<p>Factory halls lean on sweeping robot arms. The temptation is to let an arm rake all lanes so the room feels dangerous. We banned that. Every indoor arm pattern must keep a safe lane open for the entire sweep. Motion is driven from distance along the track, not from a pure timer, so holding Shift does not invent a new geometry. Outdoor stretches run with less fog and a brighter sky; when an arm shows up near a hall exit, it still follows the same safe-lane rule so the lighting change does not feel like a hitbox change.</p>
<h2>Keyboard contract</h2>
<p>Fair hazards need a stable input contract:</p>
<ul>
<li>A / D — lane change</li>
<li>W or Space — jump-flip over low trouble</li>
<li>S — ground roll when staying low is smarter</li>
<li>Shift — boost with no cooldown (tap about 1.5s toward 370, hold to stay there, release eases back in about 1s)</li>
</ul>
<p>Keeping boost identical indoors and outdoors matters more than it sounds. Dodge windows are muscle memory. If boost decay changed between biomes, the press audit would be lying about what “several speeds” means in play.</p>
<p>Browser tabs drop frames. A short lane-change buffer forgives that without letting players stack a whole combo through a press row. We treat the buffer as a jitter patch, not a skill feature.</p>
<h2>What the web export would not let us lean on</h2>
<p>Godot’s Compatibility renderer keeps the desktop browser audience wide and gives up heavier lighting. The no-threads web template lets the build sit on a plain static host without COOP/COEP headers. Together they mean we do not burn frame time on runtime probes or flashy post-process “tells.” Telegraph comes from silhouette, motion, and a readable stroke — not from a bloom flash we cannot afford.</p>
<p>Side nozzle mist stays snapped to model markers, and hover jets under the car stay on. Those cues sell flight; they do not rewrite collision.</p>
<h2>Fair runs make the rest of the loop readable</h2>
<p>Once players trust the stamp, the collecting loop can stay strict. Five parts (body, chip, motor, thrust, energy) set cars-built to the minimum of the five, and the HUD paints the weakest bin red. After a run, a stock-style results line dips where damage landed. In the garage, ten skins wait for a CONFIRM press. Those systems only feel rewarding if the death that ended the run was fair.</p>
<h2>Practical notes for other web runners</h2>
<p>Prove press cycles offline at multiple speeds. Require a permanent safe lane on sweeping indoor hazards. Freeze boost and flip timing across biomes. Keep any input buffer tiny. Spend Compatibility budget on readable motion, not on runtime fairness hacks.</p>
<p>Still iterating on dwell times and arm silhouettes. If you ship Godot 4 in the browser, we would like to hear how you telegraph mechanical hazards without mobile-tier effects.</p>
<p>Desktop-only fan-made build: cgt.monster</p>
<p>Fan-made · Desktop-only · Not affiliated with any car maker.</p>
]]></content:encoded></item><item><title><![CDATA[Designing a fleet lift-off end screen for a browser runner]]></title><description><![CDATA[Suggested tags: godot, gamedev, javascript, webdev Most runners end the same way: you crash or cross the line, a panel slides in, and you press retry. That…]]></description><link>https://cgtrocketx.hashnode.dev/designing-a-fleet-lift-off-end-screen-for-a-browser-runner</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/designing-a-fleet-lift-off-end-screen-for-a-browser-runner</guid><category><![CDATA[Godot]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[webdev]]></category><category><![CDATA[indiedev]]></category><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Thu, 22 Oct 2026 15:05:22 GMT</pubDate><content:encoded><![CDATA[<h1>Building a fleet lift-off end screen for a browser runner</h1>
<p><em>Suggested tags: godot, gamedev, javascript, webdev</em></p>
<p>Most runners end the same way: you crash or cross the line, a panel slides in, and you press retry. That works, but it throws away the best moment to show players what their run produced. In CGT Rocket X Racer, a fan-made rocket-car runner that plays in a desktop browser, we tried to make the end of a run feel like a delivery instead of a game-over screen. This post covers how that end sequence is put together, the constraints a browser build puts on it, and the trade-offs we made.</p>
<p>Fan-made · Desktop-only · Not affiliated with Tesla.</p>
<h2>What the run produces</h2>
<p>The end screen only means something if the run feeds into it. During a run you collect five part types: body, chip, motor, thrust and energy. The number of cars you can build equals the lowest of the five counters. The HUD shows your weakest counter in red, so you always know which part is holding you back. This rule matters for the end screen. Since the number of cars depends on your weakest part, a row of finished cars directly shows how balanced your collecting was, not just how far you got.</p>
<h2>The sequence, step by step</h2>
<p>The end of a run happens in several steps rather than one hard cut.</p>
<ol>
<li><p><strong>Arrival.</strong> The route alternates between indoor factory halls and open air, and it finishes by flying into an end garage. The car brakes and hovers there, and the camera orbits around it. That orbit gives the player a moment to look at the car before any numbers appear.</p>
</li>
<li><p><strong>Results.</strong> The results screen draws a stock-style price line over your run, with dips where you took damage. A line chart is easier to read at a glance than a table of numbers. You can see a clean stretch, a bad collision and a recovery without reading anything.</p>
</li>
<li><p><strong>Delivery.</strong> After the results, the scene moves to the front of the garage, where a row of cars lifts off together. This is the payoff: the parts you collected become a group of vehicles leaving the factory, not a number in a corner.</p>
</li>
<li><p><strong>The garage decision.</strong> From there the player can go into the garage, browse the skin picker (ten skins), and press CONFIRM to save a choice and apply it to the car.</p>
</li>
</ol>
<h2>Why a fleet and not a single car</h2>
<p>A single car taking off looks like any victory animation. A row of cars taking off together says something different: you built these. Linking the visuals to the cars-built rule keeps the screen honest. It also gives players a clear reason to change how they play next time. If the row looks short, the red counter on the HUD already showed which part to look for.</p>
<p>There is a readability reason too. During the run, the side nozzles and the always-on underbody hover jets are the main sign that the car is flying rather than driving. Reusing the same thruster language for the lift-off at the end ties the end screen to the feel of the run, so no new visual vocabulary has to be learned.</p>
<h2>Browser constraints shaped the design</h2>
<p>The game is built in Godot and exported to the web. Two decisions from the rest of the project directly limited what the end screen could do.</p>
<p><strong>Compatibility renderer.</strong> Everything uses Godot's Compatibility renderer, which runs on more machines in the browser but gives up some heavier lighting and post-processing. So the end screen cannot depend on expensive effects for impact. It depends on motion instead: a camera orbit, a group lifting together, and thruster effects reused from gameplay.</p>
<p><strong>No-threads web template.</strong> The web build uses the no-threads template, so it runs on a plain static host without the special cross-origin headers threaded builds need. The trade-off is less room for parallel work. That pushes us to keep the end sequence light. It reuses vehicle assets and effects already loaded for the run instead of loading a separate showcase scene with its own large assets.</p>
<p><strong>Effects in the right place.</strong> We spend effects where players actually look. During development the side nozzles were moved back behind the front wheel arch, and a check tool confirms every mist emitter sits exactly on its marker in the model. The same discipline applies to a row of cars: if emitters drift off their markers, the mistakes repeat across every car in the row and get much easier to spot.</p>
<h2>Fairness upstream, payoff downstream</h2>
<p>An end screen that celebrates the run only works if players believe the run was fair. The hazards along the way are giant presses and sweeping robot arms, plus several debuffs. An automated audit tests every press pattern across its full cycle at several speeds. Pickups are never placed inside an obstacle's motion, and indoor arm patterns always leave a safe lane. When players trust that a missing part was their own mistake, a short row at the end works as motivation instead of feeling unfair.</p>
<h2>Pacing: keeping it skippable in spirit</h2>
<p>Runners live on quick retries, and a long outro gets in the way. We keep the end sequence in short, separate steps (arrival, results, delivery, garage), so each step does one job and players always know what they are looking at. The results chart carries the information. The lift-off is the reward. The garage is the next decision.</p>
<h2>What we would tell other browser devs</h2>
<ul>
<li>Connect the end screen to a rule the player already understands. Ours is "cars built = lowest part counter".</li>
<li>Under the Compatibility renderer, motion and camera are cheaper than post-processing and read better anyway.</li>
<li>Reuse in-run assets for the outro so a lightweight web build doesn't have to load a showcase scene.</li>
<li>Use tooling to check effect placement. Small offsets multiply when you show many vehicles at once.</li>
<li>Show performance as a shape, not a spreadsheet. A line with dips is quick to read.</li>
</ul>
<p>We are still tuning the order and timing of these steps, and feedback from people building web runners is very welcome.</p>
<p>If you want to see the sequence yourself, the fan-made game runs in a desktop browser at <a href="http://www.cgt.monster">www.cgt.monster</a>.</p>
<p>Fan-made · Desktop-only · Not affiliated with Tesla.</p>
]]></content:encoded></item><item><title><![CDATA[Input feel in a Godot 4 browser game: tap, hold and everything in between]]></title><description><![CDATA[When people say a game "feels good", they usually mean the gap between pressing a key and seeing something happen is small and predictable. In a browser…]]></description><link>https://cgtrocketx.hashnode.dev/input-feel-in-a-godot-4-browser-game-tap-hold-and-everything-in-between</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/input-feel-in-a-godot-4-browser-game-tap-hold-and-everything-in-between</guid><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Thu, 22 Oct 2026 05:33:33 GMT</pubDate><content:encoded><![CDATA[<p>When people say a game "feels good", they usually mean the gap between pressing a key and seeing something happen is small and predictable. In a browser that gap is easy to make worse. This is how we handle input in CGT Rocket X Racer, a fan-made 3D runner we build in Godot 4 and play in a desktop browser, and why we made those choices. These are code-level choices, not measured latency numbers.</p>
<p>Fan-made · Desktop-only · Not affiliated with Tesla.</p>
<h2>Act on the press, not the release</h2>
<p>Lane changes, jumps and rolls fire on the key-down event and ignore key-repeat echoes. If you act on release, every move lags by however long the player holds the key. If you act on echoes, holding a key sends a car skidding across three lanes. In Godot that comes down to checking <code>pressed</code> and <code>not echo</code> on <code>InputEventKey</code> in <code>_input</code>.</p>
<p>Here is the current desktop mapping:</p>
<ul>
<li><strong>A / D or Left / Right:</strong> change lane</li>
<li><strong>W or Up:</strong> jump</li>
<li><strong>S:</strong> ground roll</li>
<li><strong>Space, Shift (plus a few alternates) or mouse click:</strong> boost</li>
<li><strong>Enter:</strong> start or restart</li>
</ul>
<p>We map more than one key per action on purpose. Different players' hands land in different places, and a browser game has no settings menu they'll open before their first run.</p>
<h2>Tap vs hold on the same button</h2>
<p>Boost is the one action where duration matters, so it's the most interesting:</p>
<ul>
<li>A <strong>tap</strong> starts a short timed boost that ramps linearly up to a set peak.</li>
<li><strong>Holding</strong> keeps you at that peak for as long as the button stays down.</li>
<li>On <strong>release</strong>, speed eases back toward cruising over a brief interval.</li>
<li>There's <strong>no cooldown</strong>, and it behaves the same indoors and outdoors.</li>
</ul>
<p>The tricky part is telling a tap from a hold without adding latency. The boost starts on the press, so a tap never waits for anything. Hold mode only kicks in once the button has stayed down past a short delay. A quick tap gets the original timed burst, and a long press turns into sustained thrust without a second input. Players never wait for the game to decide what they meant.</p>
<p>We track keys, mouse and touch for boost separately, so releasing one doesn't cancel a boost that another device is still holding.</p>
<h2>Why the ramp matters as much as the input</h2>
<p>An instant jump to top speed reads as a teleport, not as thrust. A linear ramp on the way up and an ease on the way down give the camera and the eye something to follow. With no cooldown, the skill moves from waiting to timing: you feather boost to line up a gap, then let it ease off before a hazard.</p>
<p>Because the ramp starts from whatever speed you had when you pressed, boosting from a slower moment still ends at the same peak. The press always feels like it did something.</p>
<h2>Feedback in the same frame</h2>
<p>The input itself is only half of feel. The other half is how fast the game shows it heard you:</p>
<ul>
<li>Boost turns on the car's thruster effect the moment it starts, and plays a sound.</li>
<li>Jumps play a sound and start the barrel roll right away.</li>
<li>Pickups trigger a small sparkle parented to the car, so it keeps up at speed, plus a quick scale pop on the coin and a bump on the HUD counter.</li>
</ul>
<p>None of these are expensive. All of them confirm the input happened.</p>
<h2>Audio and the first interaction</h2>
<p>Browsers block audio until the page has a user gesture. We don't create any audio players until the first real tap, click or key press. That means no autoplay warnings, and no risk of the first sound arriving late because the audio context had to wake up mid-action.</p>
<h2>Making collisions feel fair</h2>
<p>A crash that looks wrong feels like an input bug, even when it isn't. Two things help:</p>
<ol>
<li><strong>Collision matches the visuals.</strong> The car uses a few oriented boxes fitted to its body, including during banking and rolls. Obstacles use boxes attached to their animated parts. What you see is what hits you.</li>
<li><strong>Bisect back to the contact moment.</strong> When a hit is detected, the car and the obstacle timing are stepped back to the moment of contact and frozen. The crash screen never shows the car halfway inside a press, so it never looks like the game ignored your last input.</li>
</ol>
<h2>Deterministic hazards make input learnable</h2>
<p>Robot arms are animated from the car's distance along the track, not wall-clock time. Safe lanes and jump windows stay the same at any speed, so what players learn in one run still works in the next. A headless audit checks every press pattern across its full cycle at several speeds and makes sure a free lane always exists. When a player fails, it's their timing, not a hidden trap.</p>
<h2>Touch is there, but we design for desktop</h2>
<p>The same code also reads swipes: left and right to change lane, up to jump, down to roll. A touch held down counts as a boost hold. We play-test and tune on desktop, and that's what we recommend for playing.</p>
<h2>Checklist</h2>
<ol>
<li>Act on key-down, ignore echoes.</li>
<li>Map several keys to each action.</li>
<li>Start long actions on press and decide tap vs hold afterwards, so a tap never waits.</li>
<li>Give every input visible and audible feedback right away.</li>
<li>Delay audio setup until the first gesture.</li>
<li>Make collision and hazard timing deterministic and auditable.</li>
</ol>
<p>If you'd like to feel the tap and hold boost yourself, CGT Rocket X Racer is playable in a desktop browser at <a href="https://cgt.monster/x/hashnode2">https://cgt.monster/x/hashnode2</a>.</p>
<p>Fan-made · Desktop-only · Not affiliated with Tesla.</p>
]]></content:encoded></item><item><title><![CDATA[Side nozzles vs rear boost: playtesting lateral thrusters in a fan-made desktop browser rocket-car runner]]></title><description><![CDATA[Building in public: CGT Rocket X Racer is a fan-made desktop browser flying-car / rocket-car runner. This week I'm hunting notes on side nozzles — the…]]></description><link>https://cgtrocketx.hashnode.dev/side-nozzles-vs-rear-boost-playtesting-lateral-thrusters-in-a-fan-made-desktop-browser-rocket-car-runner</link><guid isPermaLink="true">https://cgtrocketx.hashnode.dev/side-nozzles-vs-rear-boost-playtesting-lateral-thrusters-in-a-fan-made-desktop-browser-rocket-car-runner</guid><dc:creator><![CDATA[CGTRocketX]]></dc:creator><pubDate>Wed, 21 Oct 2026 17:55:50 GMT</pubDate><content:encoded><![CDATA[<p>Building in public: <strong>CGT Rocket X Racer</strong> is a fan-made desktop browser flying-car / rocket-car runner. This week I'm hunting notes on <strong>side nozzles</strong> — the lateral thrusters that shove you across a lane without a straight rear burn.</p>
<p>What I want from a short PC playtest:</p>
<ol>
<li>Does a left/right pad read as a lane tool in the first 20 seconds?</li>
<li>Do players mash it like a boost, or tap it like a cut?</li>
<li>Anything muddy in the invite gate or first HUD?</li>
</ol>
<p>Desktop-only (PC/browser). Invite unlock: <strong>48WYTRMMDW</strong><br />Play: <a href="https://www.cgt.monster/x/hash">https://www.cgt.monster/x/hash</a>  </p>
<p>Fan-made. Not affiliated with Tesla or any car brand. Desktop-only. Happy to answer design questions in comments.</p>
]]></content:encoded></item></channel></rss>