Personal site · systems, commissioning, AI

Designing a Quiet Interactive Homepage Field with p5.js

A look at the moving contour field on my homepage: why I built it, how it works, what changed during the design pass, and how a quieter p5.js feature ended up fitting the site better than a louder one.

One of the quietest details on my homepage is also one of the most technical: the moving contour field that sits just under the header.

It is not there to demand attention. It is there to give the page a little pulse.

From the start, I wanted something alive without making the page feel busy. I wanted something modern without falling into the usual generic AI-web look. Most of all, I wanted it to feel like it belonged with the rest of the site: monochrome, structured, tactile, and calm enough to support the work instead of competing with it.

The first version leaned a bit more obviously generative. It had an overlay label, a grid layer, a caption, more visible accent colour in the background, and a brighter pointer response. It worked, but it explained itself too much. It felt a little too eager. So I kept simplifying it.

The final version keeps the motion, keeps the interaction, and removes the explanation.

What the feature is

The homepage ribbon is a custom p5.js sketch embedded directly into the WordPress front-page template.

The markup is deliberately spare: a section wrapper, a ribbon container, and a canvas host. On the live page now, that is basically all that remains. No badge. No caption. No decorative grid layered on top.

That means the visual has to stand on its own.

How it works

The sketch creates a field of contour-like lines running across the width of the ribbon. Each band is drawn as a curve, and each point on that curve is shaped by a few layers working together:

  • Perlin noise for the slow drifting movement
  • A sine wave for the longer cresting motion
  • A second noise pass for local warp and irregularity
  • A ripple system that briefly deforms nearby lines when the field is touched or crossed

The lines are drawn in bands from top to bottom. For each band, the code steps across the x-axis, calculates a y-position, and feeds those points into curveVertex(x, y). That is what gives it the softer topographic feel instead of a sharp waveform.

The motion stays restrained because the time step is slow, the canvas is cleared each frame, and the palette comes from the site’s CSS variables rather than hardcoded colours.

In light mode, the background uses faint dark radial gradients. In dark mode, those shapes invert into softer pale glows. The component follows the site theme without turning into a completely different element.

The interaction model

The current version does not use a pointer halo anymore. It behaves more like a ripple.

When someone moves a mouse across the ribbon or touches it on mobile, the script records a ripple with normalized x/y coordinates, an age counter, and a strength value. Those ripples are stored in state.ripples, aged on every frame, and removed once they are old enough.

While a ripple is active, the code measures the distance from each contour point to the ripple centre, calculates a moving wave term and an envelope, and adds that offset back into the line position.

Put simply: touch the field, and the nearby lines wobble like liquid for a moment before settling down.

That shift mattered. An earlier version reacted more like a cursor influence or glow. I wanted it to feel more physical, so the interaction was rewritten into a proper ripple system. The effect is still quiet, but it has a better sense of touch now.

The code structure

A few implementation details make the feature reliable inside a live WordPress site:

  • The sketch lazy-loads p5.js from a CDN only if window.p5 is not already available
  • It guards against double-mounting with data-pjbull-mounted and data-pjbull-booted
  • It reads colours from CSS custom properties like --pjbull-text, so it follows the site theme automatically
  • It resizes with the container by reading the ribbon bounds and calling resizeCanvas(...)
  • It uses pixelDensity(1) to keep rendering light and predictable

None of that is especially dramatic, but it is what makes the difference between an experiment and a homepage feature that behaves properly in production.

What changed on the way to the final version

The finished result looks simple because quite a lot was removed.

I started with a richer treatment: overlay copy, a micro-label, a grid texture, accent-tinted particles, and a more obvious interaction zone. Then I kept tightening it.

The text came off.
The index and grid layer came off.
The accent colour was reduced into monochrome.
The pointer highlight disappeared.
Then the interaction was reworked into ripples so touch on mobile felt deliberate rather than accidental.

That reduction was really the design work.

A quick nod to the influences

This feature owes something to a few different places: generative contour studies, topographic line drawings, quieter signal-field interfaces, and the broader p5.js tradition of building motion from simple rules instead of heavy assets.

On the product side, it also owes a nod to the calmer end of the AI-web aesthetic, especially interfaces that use motion as atmosphere rather than decoration. Some of the broader site restraint was shaped by what I liked in x.ai’s direction: clarity, control, and enough movement to give the page life without turning it into a performance.

The point was never to copy any one source. It was to borrow the right instincts: keep it minimal, keep it structural, and make the motion earn its place.

The tools behind it

This piece of the site came together through a mix of direct theme editing, WordPress template work, and iterative visual refinement. The main open-source tool behind the feature itself is p5.js, which made it easy to build the field as a lightweight sketch instead of a heavy animation asset.

The implementation also leaned on the flexibility of the WordPress block theme system, since the sketch was embedded straight into the front-page template rather than bolted on as a separate app.

Why it matters

A homepage feature like this can easily become filler. I did not want that.

I wanted it to set the tone quickly. To suggest systems, interaction, and craft without needing to explain itself. To add motion without clutter. To respond when touched. Then to step back and let the rest of the page do its job.

That was the standard from the beginning.

KEEP READING

More notes on websites, systems, and practical AI work.

This blog follows the work as it happens: what is being built, what gets in the way, and what turns out to be worth keeping.