Services

PlayCanvas · UI design & implementation

3D in the browser, with UI that keeps up.

Interfaces for PlayCanvas projects, in the engine’s own element system or as an HTML layer over the canvas, sized for a link that has to load fast on a phone.

Element & Screen componentsESM scriptsHTML overlay UIWebGL2 & WebGPUEditor or engine-only

Live · PlayCanvas engine

Running now,
in this page.

A PlayCanvas scene with an HTML interface over it. The callouts are DOM elements projected from points on the model every frame.

Weapon Falchion of Caen

Drag the item. The labels are HTML, pinned to points on the model.

PlayCanvas engine · loads on demand

Built for this page with the open-source PlayCanvas engine. Primitive geometry, two lights, no downloaded assets.

What we do in PlayCanvas

Built the way the
engine expects.

Native frameworks, used as intended, so your engineers inherit something familiar.

Two UI routes

Screen and Element components when the UI must live in the scene; HTML and CSS over the canvas when text, layout and accessibility matter more.

World-anchored UI

Callouts, markers and tooltips that track a point in 3D space and stay readable as the camera moves.

Input everywhere

Mouse, touch, keyboard and gamepad through one action layer, with touch targets sized for a thumb.

Load and frame budget

Lazy-loaded engine and assets, texture atlases, and UI that does not force a redraw of the scene.

In code

The same component,
in PlayCanvas.

The same component in every runtime: a health bar whose ghost segment waits 450 ms, then drains over 600 ms. It is the bar running in the demo at the top of this page.

health-bar.mjsJavaScript · ESM script
// Attached to a Group element under a Screen; fill and ghost are Image elements.
import { Script, math } from 'playcanvas';

export class HealthBar extends Script {
    static scriptName = 'healthBar';

    /** @attribute @type {import('playcanvas').Entity} */ fill;
    /** @attribute @type {import('playcanvas').Entity} */ ghost;
    /** @attribute */ ghostDelay = 0.45;
    /** @attribute */ ghostTime = 0.6;

    target = 1; shown = 1; hold = 0;

    setHealth(pct) {
        pct = math.clamp(pct, 0, 1);
        if (pct < this.target) this.hold = this.ghostDelay;
        this.target = pct;
        this.fill.element.anchor = [0, 0, pct, 1];
    }

    update(dt) {
        if ((this.hold -= dt) > 0) return;
        const step = dt / this.ghostTime;
        this.shown += math.clamp(this.target - this.shown, -step, step);
        this.ghost.element.anchor = [0, 0, this.shown, 1];
    }
}

A trimmed sample to show structure and approach. Compare all five runtimes →

Covered as standard

  • PlayCanvas engine 2.x
  • Editor projects and engine-only builds
  • Element UI and HTML overlay
  • Responsive from phone to desktop
  • Playable ads and embedded web builds
  • Analytics and deep links

Other runtimes

The design travels.

The design system and motion spec stay the same. Only the implementation changes.

PlayCanvas

Show us the build.

Send a screenshot, a recording or a build, with your target platforms and dates. You will get a straight answer on fit and scope.