LightMap

Bumped Surface Lighting 2.1 — © 1997-1999 Christian Cohnen

The LightMap applet takes a color texture and a grayscale bump map to calculate a real-time lit surface using 2D bump mapping. A radial light source follows the mouse cursor across the canvas; when the mouse leaves, the light orbits automatically along a smooth elliptical path. The bump map is converted into per-pixel x/y gradient shifts via finite differences, and for each pixel the light intensity is looked up from a pre-computed radial falloff table. The final color is the texture color multiplied by that intensity — all done per frame using integer shifts for speed, producing true-color output.

Bump mapping with dynamic light sources was a signature effect of the Atari ST, Amiga and early PC demoscene in the mid-to-late 1990s. On the 68000 CPU the per-pixel gradient lookup and light multiply were carefully arranged to avoid expensive MUL instructions. This Java applet follows the same philosophy: the >> 8 shift replaces a division by 256 in the per-pixel lighting, and the radial falloff is read from a pre-computed lookup table rather than calculated on the fly.

Originally a Java applet (1997). The JavaScript/Canvas port uses the same bump mapping algorithm but renders into an HTML5 <canvas> via ImageData. Texture and bump map are loaded as standard <img> elements.

Logo texture + Bump map => Lit output

Features

Applet Parameters

NameTypeDescriptionDefault
picURLColor texture image (GIF or JPEG)
bumpURLGrayscale bump/height map image
linkURLLink target when applet is clicked
movementflagIf present, light auto-orbits
lightSizeintDiameter of the light source (4–256)128
ambientintAmbient light level (0–128)0

Parameters (LIGHTMAP_CFG)

NameTypeDescriptionDefault
lightSizeintDiameter of light source (4–512)128
ambientintAmbient light level (0–128)0

How It Works

On startup the effect pre-computes a radial light falloff table:

for each (x, y) in lightSize x lightSize:
  r = distance² from center
  if r < radius²:
    field = (1024 - 1024 * r / radius²)² >> 12
  else:
    field = 0

The bump map is converted to gradient vectors using finite differences:

xShift[pos] = (height[pos+1] - height[pos-1]) / 2
yShift[pos] = (height[pos+w] - height[pos-w]) / 2

Each frame, for every pixel the light position is offset by the bump gradient, then the pre-computed falloff value is used to shade the texture color:

intensity = lightBall[lightPos - bumpShift]
r = (intensity * textureR[pos]) >> 8
g = (intensity * textureG[pos]) >> 8
b = (intensity * textureB[pos]) >> 8

Applet Code Example

<applet archive="LightMap.jar"
  code="LightMap.class" width="320" height="150">
  <param name="pic" value="logo.jpg">
  <param name="bump" value="bump.jpg">
  <param name="movement">
  <param name="link" value="https://www.chriscohnen.de">
</applet>

JavaScript Canvas Version

<canvas id="game" width="640" height="300"></canvas>
<img id="texImg" src="logo.jpg" style="display:none">
<img id="bumpImg" src="bump.jpg" style="display:none">
<script src="lightmap.min.js"></script>