Skip to content
Techniques & Technology

Dirty Rectangles

Efficient screen updates

Dirty rectangle rendering optimised screen updates by tracking and redrawing only the areas that changed, saving CPU cycles when most of the display remained static.

commodore-amigaibm-pccommodore-64graphicsoptimisationrendering

Why redraw the entire screen when only a small part changed? Dirty rectangle rendering tracks which screen regions have changed and redraws only those areas. For games with mostly static backgrounds and a handful of moving sprites — point-and-click adventures, strategy games, GUI applications — this dramatically reduces rendering work compared to redrawing every pixel every frame.

Dirty rectangles fall between double-buffering (full redraw, swap) and direct screen drawing (write while displaying, risk tearing). They give you tear-free output without the cost of touching unchanged pixels.

Fast facts

  • Purpose: Reduce unnecessary redrawing.
  • Method: Track changed screen regions as bounding rectangles.
  • Benefit: Lower CPU usage when most of the screen is static.
  • Best for: Static backgrounds with a few moving elements (adventures, GUIs, board games).
  • Worst for: Full-screen scrolling, particle systems, fast-moving full-screen action.

How it works

Each frame:

Step Action
1 Mark previous sprite/object positions as “dirty”
2 Mark new sprite/object positions as “dirty”
3 Coalesce overlapping or adjacent dirty rects
4 For each dirty rect: redraw the background, then any objects that overlap it
5 Copy only the dirty rects from back buffer to front buffer (or to display)

The dirty list is typically a small array of (x, y, w, h) rectangles, regenerated every frame.

Tracking changes

Change type Dirty region
Sprite moved Old position + new position (often coalesced if they overlap)
Animation frame change Sprite bounding box at current position
Score / HUD update Score display area
Background change Affected tiles (e.g. a door opening)
Particle effect Bounding box of all particles (or per-particle if scattered)

Rectangle operations

A small library of rectangle primitives suffices:

Operation Purpose
Union Combine two overlapping rects into one bounding rect
Intersection Find overlap area
Containment Does rect A entirely contain rect B?
Clipping Restrict rect to within screen bounds
Coalescing Merge nearby rects whose union is cheaper than two separate updates

The coalescing decision is heuristic: merge if the union area is less than the sum of the individual areas plus some overhead, otherwise keep them separate.

Pseudo-code

dirty_list = []

each_frame:
    # Phase 1: erase and remember what was where
    for sprite in moving_sprites:
        if sprite.moved or sprite.animated:
            dirty_list.append(sprite.previous_bbox())
            dirty_list.append(sprite.current_bbox())

    # Phase 2: coalesce overlapping rects
    coalesce(dirty_list)

    # Phase 3: redraw each dirty rect
    for rect in dirty_list:
        clip rect to screen
        redraw_background(rect)
        for sprite in sprites_overlapping(rect):
            draw_sprite_clipped_to(sprite, rect)

    # Phase 4: blit dirty rects to display
    for rect in dirty_list:
        blit(back_buffer, display, rect)

Advantages

Benefit Impact
CPU savings More processing time available for game logic
Reduced flicker Smaller updates blit faster, lower chance of catching the raster mid-update
Memory bandwidth Less data moved per frame; matters on shared-memory systems (Amiga chip RAM)
Scales with activity Idle screens cost nothing to “render”

Limitations

Drawback Situation
Tracking overhead Bookkeeping is non-trivial; small rects don’t pay back
Full-screen changes If everything moves, dirty-rect costs more than full redraw
Many objects Coalesce-and-redraw degenerates toward full-screen as object count rises
Scrolling The whole screen is dirty every frame; no benefit
Z-ordering Rectangles overlapping in depth need careful redraw order
Complex backgrounds Background reconstruction must be cheap, or savings vanish

When to use

Game style Dirty rects?
Point-and-click adventures (Sierra, LucasArts) Yes — ideal use case
Strategy games with mostly static maps Yes
Board games, card games Yes
GUI applications (Workbench, GEM, Windows 1-2) Yes — original motivating use case
Side-scrolling platformers No — too much movement
Vertical-scrolling shooters No
Action games with full-screen scroll No
Particle-heavy effects Marginal at best

Platform-specific notes

Platform Application
Amiga Built into the operating system: the Layers library keeps a DamageList of exposed rectangles per window, and BeginUpdate()/EndUpdate() clip redrawing to it
PC (pre-VGA) EGA-era games before fast hardware blits; Sierra and LucasArts ports
C64 Less common — sprites are hardware, so the technique mostly applies to the playfield
GUI systems The Macintosh Window Manager accumulates an “update region” and raises an update event when it is not empty; GEM AES hands the application a “rectangle list” of the parts of a window that need redrawing
HTML5 Canvas Modern web games still use this; Canvas’s clearRect + redraw pattern is dirty-rect rendering

Where the technique was written down

The clearest period descriptions come from windowing systems rather than games, because overlapping windows made partial redraw a system service. Apple’s 1984 Window Manager documentation describes an “update region” that “the Window Manager keeps track of… and accumulates” as windows are uncovered, with the application asked to redraw only when the region is not empty. GEM on the Atari ST exposes the same idea as a rectangle list: James Luczak’s ST-Log tutorial (January 1987) walks through fetching each rectangle with WIND_GET and redrawing “the correct number of rectangles” in a loop. On the Amiga, the ROM Kernel Reference Manual’s Layers chapter documents the DamageList and the BeginUpdate()/EndUpdate() pair that installs it as the clipping list, so that “only the damaged areas are actually rendered into, saving time”.

Modern relevance

Dirty rectangles remain alive in:

  • Web rendering — browsers track dirty regions for repaint optimisation.
  • Game engines — UI systems use dirty-rect updating (Unity UGUI, Unreal Slate).
  • GUI toolkits — Qt, GTK, AppKit/UIKit all use damage-region tracking under the hood.
  • Compositing window managers — every modern window manager tracks per-window damage rectangles.

The terminology has evolved (now “damage tracking” or “invalidation”) but the core idea is unchanged from the 1980s.

See also

Not yet fact-checked. This entry was drafted by an AI and nobody has verified it. The dates, figures and technical details may be wrong. Use it to find your bearings, then confirm anything that matters against a primary source.