Onceworlds
Engine · 2D Looks

2D looks: lights, effects and post

Lights, shadows, hit flashes, outlines, glows, screen looks and shaders in 2D, one line each.

A 2D game on the engine draws on the GPU by itself (Canvas 2D only where a browser has no WebGL2), so it can afford lights, shadows, glows and screen effects even on a phone. Each takes one line: a look on the camera, lights in the world, effects on the things that need them. The full reference is the API's "2D on the GPU" part.

One line for each

You want Write
A night level Lighting2D.night() on the camera; Light2D.lamp() at each lamp
A dungeon lit by torches Lighting2D.dungeon() on the camera; Light2D.torch() at each torch; Occluder2D() on the walls (their Tilemap layer, shapes or sprites)
Neon Lighting2D.neon() and PostFx2D.neon() on the camera; Light2D.neon('#ff2bd6'); Material2D.glowing() on signs
The player carries a light player.add(Light2D({ radius: 5 })), or a flashlight: a child with Light2D.spot({ direction: 0 }) that turns with the player
A hit flash flash2D(enemy) (any drawable; it fades back by itself)
An outline Material2D.outlined('#ffffff')
The same picture in other colours (a team, a stronger enemy) Material2D.recolored({ '#3b82f6': '#ef4444' }): each colour becomes its value, up to 4
A thing that glows in the dark Material2D.glowing(), and Light2D.glow('#ffd23f') so it lights what is around it
Burn away on death tween(e, Material2D, { dissolve: 1 }, { ms: 500, onDone: () => e.despawn() }) on an entity with Material2D()
Grass, flags, water that sway Material2D.swaying()
Sprites with depth under moving lights Material2D({ normalMap: 'assets/hero_n.png' }) (a normal map laid out like the picture)
A soft, finished look PostFx2D.soft() on the camera
Retro pixels, an arcade screen PostFx2D.retro(), PostFx2D.crt()
A mood PostFx2D.noir(), .horror(), .sunset(), .dream()
// @run2gpu
import { createGame, Transform } from '@onceworlds/engine';
import { Render2D, Shape2D, Light2D, Lighting2D, Occluder2D, PostFx2D, Material2D, camera2D } from '@onceworlds/engine/modules';

const game = createGame({
  space: '2d',
  headless: true,
  modules: [Render2D()],
  scene(world) {
    world.spawn([Transform(), camera2D({ height: 12, clear: '#08090f' }), Lighting2D.night(), PostFx2D.soft()]);
    const player = world.spawn([Transform(), Shape2D({ shape: 'circle', radius: 0.4, fill: '#4ee1ff' }), Light2D({ radius: 5 })]);
    world.spawn([Transform({ position: [3, 0, 0] }), Shape2D({ shape: 'rect', size: [1, 4], fill: '#2a3048' }), Occluder2D()]);
    world.spawn([Transform({ position: [-4, 2, 0] }), Shape2D({ shape: 'roundRect', size: [2.4, 0.8], radius: 0.2, fill: '#ff2bd6' }), Material2D.glowing(), Light2D.neon('#ff2bd6')]);
    void player;
  },
}).start();
game.run(0.1);
expect(game.errors).toEqual([]);

Rules of thumb