Resources

Troubleshooting

The handful of things that trip people up — each with the why and the fix. Most reduce to one of two facts: the field reads bodies on a schedule, and it refuses to fake motion.

New elements aren't reacting

The field scans for [data-body] elements when it mounts. If you add or remove bodies afterwards — a route change, a list update, a modal — it won't see them until you re-scan. Call field.scan() (or its alias rescan()) after the DOM settles.

Rescan
// plain DOM / web component
field.scan();

// React — bodies render after the field mounts, so scan from onReady
<FieldField onReady={(field) => field.scan()} />

The field is frozen or blank

Two behaviours pause the loop on purpose — both are features, not bugs:

  • Reduced motion. Under prefers-reduced-motion: reduce the simulation renders a single static frame (dt = 0) and never animates. Test with the OS setting off to see motion.
  • Backgrounded tab. The loop stops on visibilitychange and resumes when the tab is foregrounded again, so a hidden tab costs nothing.
  • Off-screen cells. A <field-cell> gates its loop on an IntersectionObserver — it only runs while visible.

The canvas covers my content or eats clicks

With <field-root> and the React component this is handled for you. If you own the canvas via createField, position it fixed behind your content and let pointer events pass through:

CSS
/* the canvas is decorative — behind content, never intercepting clicks */
.forces-canvas {
  position: fixed;
  inset: 0;
  z-index: 0;          /* your content sits above */
  pointer-events: none;/* clicks pass through */
}
One field per page. There is exactly one background field. Don't mount several <field-root> elements — for small in-frame effects use <field-cell> instead, which is built to be embedded many times.

--d isn't updating my element

Density write-back is opt-in. The element needs data-feedback, and your CSS should default --d so it renders before the first write. The value is low-pass filtered (~0.2s), so it eases rather than jumps.

HTML + CSS
<!-- 1. opt the element in -->
<h1 data-body="attract" data-range="320" data-feedback>weighty</h1>

<style>
  h1 {
    --d: 0;  /* the engine eases this in ∈ [0,1]; default it yourself */
    font-variation-settings: 'wght' calc(380 + var(--d) * 420);
  }
</style>
Words glow, they are never drawn. The engine never assembles particles into letterforms — text stays real, selectable, and accessible; --d drives weight, glow, and scale on the real element.

SSR & hydration

The field is a client-only concern. The custom element registers in the browser, and the React component mounts its canvas on the client — there is no server-rendered canvas to mismatch. In Astro, the import '@field-ui/elements' lives in a client <script>; in Next.js, render the field in a client component. Bodies are plain markup, so they server-render fine; only the field's reaction is deferred to the client.

It feels heavy on a large page

The field is one shared canvas, so cost does not grow with the number of bodies beyond the per-body loop term. If a very large or low-power surface needs headroom, lower densitydensity: 0.5 halves the particle count. See Performance for the full picture.

Still stuck? The Lab fires a single particle into any force and checks it against its law — the fastest way to confirm a force behaves as documented.