On this page
  1. Related
  2. Games
  3. References
  4. Sources

A video pinball game adds gravity to the ball each frame, tests what it touches, and turns the touch into a bounce. Pokémon Pinball tests 16 points around the ball against 1-bit masks and looks the surface’s angle up from the run of points that touch1. It bounces the ball back at a quarter of its speed into the wall2.

  • Masks and a normal tablePokémon Pinball (1999). 16 points around the ball tested against 1-bit masks; the touching run picks a normal from a 256-entry table, and the bounce keeps a quarter of the speed into the wall.3,4,1,2

Games

GameYearHowNote
Pokémon Pinball1999Masks and a normal table16 points around the ball tested against 1-bit masks; the touching run picks a normal from a 256-entry table, and the bounce keeps a quarter of the speed into the wall
Every claim about this mechanic, by game (4)
Pokémon Pinball 1999In the US and Australian Pokémon Pinball, the main loop runs the game's screen logic once per frame. During play that logic adds gravity, caps the velocity, handles the tilts and, on the lower half of a table, the flippers. It then tests for collisions, bounces the ball if one touches, and moves the ball last.Code
Each axis of the ball's velocity is capped on its own: a high byte above 7 becomes 7 and one below −7 becomes −7, the fraction kept, so the caps are 7 255/256 px per frame down or right and −7 up or left (computed), and a diagonal ball goes faster than a straight one. The move is limited again: a velocity of 5 px per frame or more moves the ball 4 255/256 px that frame, and one below −4 moves it 4 255/256 px the other way.Code
To find a wall, the game tests 16 points around the ball against 1-bit collision masks for the tiles under it. The first and last points of the longest run of touching points index a 256-entry table, CollisionForceAngles, for the surface's normal angle, in 256ths of a turn. Two more tables with the same index push the ball back out of the wall.Code
On a hit, the game rotates the ball's velocity so the surface's normal points up. A ball moving away is left alone. A ball moving in bounces back at a quarter of its speed into the surface, plus an eighth for each step of the hit's amplification. Its speed along the surface gains half the stored spin, in 256ths of a pixel per frame; the new spin is that speed × 4 / 256. The velocity is rotated back. A speed of 3 px per frame or more into the surface also sets the rumble's duration to 1 and, unless a flipper was hit, plays a sound.Code

References

  1. ^ abTo find a wall, the game tests 16 points around the ball against 1-bit collision masks for the tiles under it. The first and last points of the longest run of touching points index a 256-entry table, CollisionForceAngles, for the surface's normal angle, in 256ths of a turn. Two more tables with the same index push the ball back out of the wall.Codepret/pokepinball
    How we know
    2735	ld b, 16  ; length of wCollisionPointTests
    
    home.asm line 2735 at 0a6f6dc · upstream ↗
    2878.determinedCollisionStatus
    2879	; e = maximum number of consecutive successful collision points
    2880	; d = the start and last index of that consecutive string of collision points (wCollisionPointTests)
    2881	;     the hi nybble is the start index, lo nybble is the end index
    ⋮
    2892	; Based on the start and end collision point indices, look
    2893	; up the corresponding normal angle for them.
    ⋮
    2896	ld hl, CollisionForceAngles
    2897	add hl, de
    2898	ld a, [hl]
    2899	ld [wCollisionNormalAngle], a
    2900	; Again, based on the start and end collision point indices, apply
    2901	; position offsets to position the ball safely outside of the wall
    ⋮
    
    home.asm lines 2878–2922 at 0a6f6dc · upstream ↗
    2345Cosine: ; 0x2147
    ⋮
    2349	add $40
    ⋮
    2357	and $7f ; ensure angle is between 0 and 180 degrees
    
    home.asm lines 2345–2357 at 0a6f6dc · upstream ↗
    1CollisionForceAngles: ; 0x8817
    2; This data has something to do with collisions.
    3	db $C0
    
    data/collision/collision_deltas.asm lines 1–3 at 0a6f6dc · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  2. ^ abOn a hit, the game rotates the ball's velocity so the surface's normal points up. A ball moving away is left alone. A ball moving in bounces back at a quarter of its speed into the surface, plus an eighth for each step of the hit's amplification. Its speed along the surface gains half the stored spin, in 256ths of a pixel per frame; the new spin is that speed × 4 / 256. The velocity is rotated back. A speed of 3 px per frame or more into the surface also sets the rumble's duration to 1 and, unless a flipper was hit, plays a sound.Codepret/pokepinball
    How we know
    101	ld a, [wIsBallColliding]  ; check for collision flag
    ⋮
    106	; First, rotate the velocity vector into a standardized coordinate
    107	; system where the normal angle points upwards.
    108	ld a, [wCollisionNormalAngle]
    109	call RotateVector
    110	; Apply the collision forces to the ball in the rotated coordinate system.
    111	call ApplyCollisionForces
    ⋮
    144	; Rotate the velocity vector back to the regular coordinate system, and
    145	; set the ball's velocity to the resulting vector.
    146	ld a, [wCollisionNormalAngle]
    147	call NegateAngleAndRotateVector
    148	call SetBallVelocity
    
    engine/pinball_game.asm lines 101–148 at 0a6f6dc · upstream ↗
    2528ApplyCollisionForces: ; 0x222b
    ⋮
    2542	; Early exit if the ball is traveling away from the collision normal.
    2543	bit 7, d
    2544	jr nz, .exit
    ⋮
    2547	cp 3
    2548	jr c, .applyforces
    2549	; The ball collided hard because its y velocity is a
    2550	; large value. Shake the rumble pack, and play a little
    2551	; sound effect to enhance the collision.
    2552	ld a, $ff
    2553	ld [wRumblePattern], a
    2554	ld a, 1
    ⋮
    
    home.asm lines 2528–2625 at 0a6f6dc · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  3. ^In the US and Australian Pokémon Pinball, the main loop runs the game's screen logic once per frame. During play that logic adds gravity, caps the velocity, handles the tilts and, on the lower half of a table, the flippers. It then tests for collisions, bounces the ball if one touches, and moves the ball last.Codepret/pokepinball
    How we know
    2114.master_loop
    2115	call TickRumbleDuration
    2116	call DoScreenLogic
    ⋮
    2119	rst AdvanceFrame
    2120	jr .master_loop
    
    home.asm lines 2114–2120 at 0a6f6dc · upstream ↗
    69GameScreenFunction_HandleBallPhysics: ; 0xd909
    70; main loop for stage logic
    ⋮
    74	call ApplyGravityToBall
    75	call LimitBallVelocity
    ⋮
    78	call HandleTilts
    ⋮
    81	callba nz, HandleFlippers  ; only perform flipper logic on the lower half of stages
    ⋮
    86	call CheckStageCollision
    ⋮
    91	call CheckGameObjectCollisions
    ⋮
    111	call ApplyCollisionForces
    ⋮
    150	call MoveBallPosition
    
    engine/pinball_game.asm lines 69–150 at 0a6f6dc · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  4. ^Each axis of the ball's velocity is capped on its own: a high byte above 7 becomes 7 and one below −7 becomes −7, the fraction kept, so the caps are 7 255/256 px per frame down or right and −7 up or left (computed), and a diagonal ball goes faster than a straight one. The move is limited again: a velocity of 5 px per frame or more moves the ball 4 255/256 px that frame, and one below −4 moves it 4 255/256 px the other way.Codepret/pokepinball
    How we know
    2394LimitBallVelocity: ; 0x2180
    2395; Ensures that the ball's x and y velocity are kept under a threshold.
    2396; The ball can travel at a greater max speed when moving diagonally, since it
    2397; limits the x and y components independently.
    ⋮
    2406	cp 8
    2407	ret c
    2408	ld a, 7  ; max positive velocity
    ⋮
    2410	ret
    ⋮
    2413	cp -7
    2414	ret nc
    2415	ld a, -7  ; max negative velocity
    
    home.asm lines 2394–2415 at 0a6f6dc · upstream ↗
    2436AddVelocityToPosition: ; 0x21c3
    2437	ld a, [de]
    2438	bit 7, a
    2439	jr nz, .negativeVelocity
    2440	cp 5
    2441	jr c, .readLoByte
    2442	ld bc, $04ff
    2443	jr .updateBallPos
    ⋮
    2445.negativeVelocity
    2446	cp -4
    2447	jr nc, .readLoByte
    2448	ld bc, -$04ff
    ⋮
    
    home.asm lines 2436–2458 at 0a6f6dc · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code