Phaser AE

Blob Shadows

Free packv3.0.0-33DLook

Contact shadows: a soft dark ellipse on the ground under every object, drawn in one instanced pass with no shadow map and no second pass over the scene. It does not imitate a real shadow; it solves the problem a scene without shadows has, which is that you cannot tell whether an object is touching the ground or floating above it.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

Blob Shadows: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

blobShadows3d

function blobShadows3d(opts: BlobShadows3dOptions = {}): Pack<{ blobShadows: BlobShadows3dApi }>

The blob-shadows-3d pack: contact shadows as soft ellipses, one instanced draw for the whole world. Requires a 3D world and render-3d, whose depth it tests against and whose camera it shares.

Functions

blobParams

function blobParams( fw: number, fd: number, bottom: number, opts: { max: number; fade: number; alpha: number }, force = false, ): { rx: number; rz: number; alpha: number } | null

The blob for one object: per-axis ellipse radii and an alpha, or null when it casts none.

A blob is a CONTACT shadow, so three things disqualify a caster: a footprint too big to be an object at all (a floor, a plaza), sitting below the ground, and floating higher than fade — a flying or stacked object projecting a hard offset ghost onto the ground reads as a bug, not as a shadow.

The half-extent is 0.6 of the footprint, so the full span is 1.2 times the caster: a soft-edged ellipse that hugs the shape with a slim feathered rim. A wider one balloons past every object and paints over its neighbours' feet. It tightens further as the caster rises.

Types

BlobShadow

interface BlobShadow

A blob placed by hand. Every field is live.

  • x: number — World x.
  • z: number — World z.
  • radiusX: number — Half-extent along x.
  • radiusZ: number — Half-extent along z.
  • yaw: number — Rotation, radians.
  • alpha: number — Darkness 0..1.
  • kill(): void — Remove it.
  • readonly alive: boolean — True until killed.

BlobShadows3dApi

interface BlobShadows3dApi

game.blobShadows.

  • alpha: number — Darkness at full contact, live.
  • max: number — The widest footprint that still casts, live.
  • fade: number — The height at which a blob has faded out, live.
  • groundAt(fn: ((x: number, z: number) => number) | null): void — Give it the ground to sit on — a terrain's heightAt.
  • add(spec: { x: number; z: number; radiusX: number; radiusZ?: number; yaw?: number; alpha?: number }): BlobShadow — Make one by hand that follows nothing — a decal you place yourself.
  • enabled: boolean — Turn the whole pass off.
  • readonly drawn: number — How many blobs were drawn last frame.

BlobShadows3dOptions

interface BlobShadows3dOptions

How the blobs look. Every field is live.

  • alpha?: number — Darkness at full contact, 0..1. Default 0.38.
  • max?: number — Footprints wider than this cast nothing — a floor or a plaza is not a caster. Default 12.
  • fade?: number — Height above the ground at which a blob has faded away entirely. Default 6.
  • groundAt?: (x: number, z: number) => number — The ground height under a point. Default a flat plane at 0.
  • lift?: number — Clearance above the ground, so the quad never z-fights it. Default 0.02.
  • tag?: string — Tag to look for; objects without it cast nothing. Default every 3D object with a mesh.
  • enabled?: boolean — Whether the pass runs at all. Default true.