<?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: PatilRB</title>
    <description>The latest articles on DEV Community by PatilRB (@patilrb).</description>
    <link>https://dev.to/patilrb</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%2F4083431%2Ffec512ba-ed01-4fd6-b8e2-c7bae032d764.webp</url>
      <title>DEV Community: PatilRB</title>
      <link>https://dev.to/patilrb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/patilrb"/>
    <language>en</language>
    <item>
      <title>The dual prompt: why your AI task handoffs keep failing</title>
      <dc:creator>PatilRB</dc:creator>
      <pubDate>Tue, 18 Aug 2026 15:28:01 +0000</pubDate>
      <link>https://dev.to/patilrb/the-dual-prompt-why-your-ai-task-handoffs-keep-failing-2a0o</link>
      <guid>https://dev.to/patilrb/the-dual-prompt-why-your-ai-task-handoffs-keep-failing-2a0o</guid>
      <description>&lt;p&gt;A while back a teammate ran a task I wrote and it came back wrong. Not subtly wrong — it had refactored a file I never mentioned. The model was fine. My prompt was the problem: I had written one set of instructions for two completely different readers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two readers, one prompt
&lt;/h2&gt;

&lt;p&gt;When you hand an AI task to someone else — they run it on their own Claude Code or Codex subscription and send you the result — there are two audiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The human&lt;/strong&gt; needs to know what this is, why it matters, what "done" looks like, and what to sanity-check before sending it back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent&lt;/strong&gt; needs exact scope, hard constraints, which files or systems are in play, and what it must not touch.&lt;/p&gt;

&lt;p&gt;A single prompt serving both ends up too vague for the agent and too jargon-dense for the person. And the second failure is the expensive one: if the person can't tell whether the output is right, they forward it unchecked. Your review becomes the only gate, which defeats the point of delegating at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the split looks like
&lt;/h2&gt;

&lt;p&gt;Here is the version I used to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clean up the analytics module and make sure the tests pass.
Should be quick.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the same task written for both readers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## For you (the human)&lt;/span&gt;
We're removing the last of the legacy event names before the
rename ships Friday. There are 11 call sites.

Done looks like: &lt;span class="sb"&gt;`rg "track_legacy" src/`&lt;/span&gt; returns nothing, and
the analytics test suite is green.

Before you send it back, check the diff does not touch
src/analytics/schema.ts — another team owns that file and
changing it will block the release.

&lt;span class="gu"&gt;## For the agent&lt;/span&gt;
Scope: src/analytics/&lt;span class="ge"&gt;**&lt;/span&gt;, excluding src/analytics/schema.ts

Task: replace every &lt;span class="sb"&gt;`track_legacy(&amp;lt;name&amp;gt;)`&lt;/span&gt; call with
&lt;span class="sb"&gt;`track(&amp;lt;name&amp;gt;)`&lt;/span&gt;, preserving arguments and ordering.

Constraints:
&lt;span class="p"&gt;-&lt;/span&gt; Do not modify schema.ts
&lt;span class="p"&gt;-&lt;/span&gt; Do not reformat or touch unrelated lines
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`npm test -- analytics`&lt;/span&gt;; report failures rather than
  fixing ones outside this scope

Out of scope: renaming events, the dashboard package.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second is four times longer. It is also the difference between a delivery you can merge and one you have to redo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "for you" half matters more than it looks
&lt;/h2&gt;

&lt;p&gt;The reflex is to skip it. The human is just going to paste the agent half anyway, right?&lt;/p&gt;

&lt;p&gt;But the human half does something the agent half cannot: it gives the person enough context to &lt;strong&gt;reject&lt;/strong&gt; a bad result. An agent will confidently hand back something plausible. If the person running it has no idea what correct looks like, plausible and correct are indistinguishable. One sentence — "check the diff does not touch schema.ts" — turns a passive relay into an actual reviewer.&lt;/p&gt;

&lt;p&gt;That is the whole return on writing the second half.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint underneath
&lt;/h2&gt;

&lt;p&gt;There is a reason this pattern comes up at all. You cannot hand someone else's Claude or Codex subscription a task without handing over the login. So the work has to travel as &lt;em&gt;instructions&lt;/em&gt;, not as access — and that is exactly why the writing quality carries so much weight. With shared access you can course-correct mid-run. With a handoff, the prompt is the entire interface.&lt;/p&gt;

&lt;p&gt;We have been building &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt; at Rockbite Games around this shape of work (disclosure: I work there). The dual prompt is a pattern we kept re-deriving by hand until we made it the default structure of a task. If you want the longer reasoning there is a &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;write-up on the dual prompt&lt;/a&gt;, and &lt;a href="https://wagglet.com/blog/wagglet-workflow-request-draft-ticket-delivery" rel="noopener noreferrer"&gt;the full request-to-delivery workflow&lt;/a&gt; if you want to see where it sits end to end. For wiring an agent in directly there is an &lt;a href="https://wagglet.com/docs/mcp" rel="noopener noreferrer"&gt;MCP endpoint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;None of that is required to use the pattern, though. It is just two headings in a markdown file.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist that mostly works
&lt;/h2&gt;

&lt;p&gt;Before sending a task for someone else to run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can they tell, without asking you, whether the result is wrong?&lt;/li&gt;
&lt;li&gt;Have you named at least one thing that must &lt;strong&gt;not&lt;/strong&gt; change?&lt;/li&gt;
&lt;li&gt;Is scope stated as paths and systems, not as a description?&lt;/li&gt;
&lt;li&gt;Have you said what to do on failure — report it, or fix it?&lt;/li&gt;
&lt;li&gt;Would this still make sense to them in three days?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those are a no, the prompt is not finished. It will come back wrong and you will blame the model.&lt;/p&gt;

&lt;p&gt;Curious whether other people have landed on the same split, or something better — particularly anyone running handoffs across a team rather than solo.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>llm</category>
      <category>devops</category>
    </item>
    <item>
      <title>Authoring particle VFX for Three.js — and the delta-time bug everyone hits</title>
      <dc:creator>PatilRB</dc:creator>
      <pubDate>Tue, 18 Aug 2026 14:31:09 +0000</pubDate>
      <link>https://dev.to/patilrb/authoring-particle-vfx-for-threejs-and-the-delta-time-bug-everyone-hits-2447</link>
      <guid>https://dev.to/patilrb/authoring-particle-vfx-for-threejs-and-the-delta-time-bug-everyone-hits-2447</guid>
      <description>&lt;p&gt;If you have added particle effects to a Three.js scene, you have probably done it the hard way: a &lt;code&gt;Points&lt;/code&gt; geometry, a custom shader, and a tweak-recompile-refresh cycle every time someone says "more sparks, less smoke."&lt;/p&gt;

&lt;p&gt;The recompile cycle is the real problem. Particle work is iterative by nature — you have to see a change to judge it. When the effect lives in code, every iteration costs a rebuild, so in practice you stop iterating long before the effect is actually good.&lt;/p&gt;

&lt;p&gt;Game engines solved this years ago by splitting authoring from runtime: design the effect in a visual tool, export it as data, load the data at runtime. That split works fine on the web too, and the runtime half is the part worth writing about.&lt;/p&gt;

&lt;p&gt;I have been using NixieFX for this lately (disclosure: I work at Rockbite Games, which builds it — it is MIT-licensed and free). The code below is Three.js specific, but the pattern is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  An exported effect is just data
&lt;/h2&gt;

&lt;p&gt;A manifest plus some JSON per effect plus texture files. Nothing you cannot inspect by hand:&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;three&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadVfxExportBundle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nixie-fx/export&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThreeVfxRenderer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ThreeVfxTextureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nixie-fx/three&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BUNDLE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/vfx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;json&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to load &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="s2"&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BUNDLE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/manifest.json`&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;effectsByPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BUNDLE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]),&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;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadVfxExportBundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;effectsByPath&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;requiredBackend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;three3d&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wire up the renderer
&lt;/h2&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;textures&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;ThreeVfxTextureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;resolveUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BUNDLE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectsById&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;impact-burst&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;textures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assets&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;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;texture&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vfx&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;ThreeVfxRenderer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;textureProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textures&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;preload&lt;/code&gt; line matters more than it looks. Skip it and the first spawn of an effect pops in a frame or two late while its texture decodes — which is exactly the moment the player is looking at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spawn it
&lt;/h2&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;burst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vfx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&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="mi"&gt;1&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;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// later, on a hit:&lt;/span&gt;
&lt;span class="nx"&gt;burst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTransform&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;position&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="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;burst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;seed&lt;/code&gt; is worth knowing about: same seed, same randomisation. Useful for deterministic replays, for screenshot tests that do not flake, and for making sure a reviewer sees the same thing you did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotcha: delta seconds, not milliseconds
&lt;/h2&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;clock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Clock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAnimationLoop&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vfx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDelta&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;   &lt;span class="c1"&gt;// seconds, not ms&lt;/span&gt;
  &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;update()&lt;/code&gt; wants &lt;strong&gt;seconds&lt;/strong&gt;. If you are used to doing your own &lt;code&gt;performance.now()&lt;/code&gt; bookkeeping you will reach for milliseconds out of habit, pass &lt;code&gt;16.7&lt;/code&gt; where &lt;code&gt;0.0167&lt;/code&gt; was expected, and every effect will fast-forward through its entire lifetime inside a single frame.&lt;/p&gt;

&lt;p&gt;The symptom is genuinely confusing, because nothing throws. You get a one-frame flash and then nothing, so it looks like the effect never played, and you go hunting in the wrong place — checking whether the bundle loaded, whether the texture resolved, whether the position is behind the camera. It is none of those. It already played, 400 times faster than you wanted.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;THREE.Clock().getDelta()&lt;/code&gt; returns seconds already, so use it and move on. Driving your own loop? Divide by 1000.&lt;/p&gt;

&lt;p&gt;While you are on that line, clamp it:&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="nx"&gt;vfx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDelta&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise the first frame after a backgrounded tab or a long GC pause arrives with a delta of several seconds, and every live effect in the scene jumps to its end state at once. Same bug, different trigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean up
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vfx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;textures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Is the split worth it
&lt;/h2&gt;

&lt;p&gt;Honest trade-off: you take on a dependency and a build step, and you get back not writing shader code plus the ability to iterate on look-and-feel in seconds. If you need exactly one simple puff of smoke, hand-rolling &lt;code&gt;Points&lt;/code&gt; is still less total work — genuinely, do that. Past two or three effects, or the moment someone who does not write code needs to adjust them, the data-driven split wins clearly.&lt;/p&gt;

&lt;p&gt;If you want to dig further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nixiefx.com/threejs-runtime/" rel="noopener noreferrer"&gt;Three.js runtime API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nixiefx.com/threejs-game-tutorial/" rel="noopener noreferrer"&gt;Full mobile 3D + particle VFX walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nixiefx.com/editor/" rel="noopener noreferrer"&gt;The editor&lt;/a&gt; — runs in the browser, nothing to install&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/azakhary/nixie-fx" rel="noopener noreferrer"&gt;Source on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions about the runtime side in the comments.&lt;/p&gt;

</description>
      <category>threejs</category>
      <category>javascript</category>
      <category>webgl</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
