A Link to the Past

Subpixels

science

This game uses subpixel-based velocity for Link's movement. This is the reason behind his changing movement speeds as he walks (ie. cardinal direction movement alternating between frames of 2px and frames of 1px).

The way this works is that Link has a "base speed" for any type of movement (from 0 to 4), and sometimes you get an extra pixel boost. If you move in the leftwards or upwards directions, the pixel boost happens as the first step in the pattern.

How Coordinates and Subpixels Are Updated

Speed

See the table below for what speed Link has in different situations.

The speed values only apply to one axis, so for diagonal movement or movement into slopes, the total speed will be higher. If you need explanations for superspeed, sword climb, or bomb climb, visit their respective links.

WhereMovementSubpixel Velocity (in hex)PatternAverage Speed (pixels/frame)
GroundCardinal0x182,1,2,11.5
GroundDiagonal0x101,1,1,11.0
GroundDashing0x404,4,4,44.0
GroundSword out, Cardinal0x142,1,1,11.25
GroundSword out, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
GroundCarrying object, Cardinal0x142,1,1,11.25
GroundCarrying object, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
GroundSuperspeed, Cardinal0x404,4,4,44.0
GroundSuperspeed, Diagonal0x2A3,3,2,3,3,2,3,22.625
GroundPushing statue, Cardinal0x0C1,1,1,00.75
GroundPushing statue, Diagonal0x101,1,1,11.0
GroundPushing statue, Sword out, Cardinal0x142,1,1,01.25
GroundPushing statue, Sword out, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
GroundPulling statue0x041,0,0,00.25
Ground, SlopeCardinal0x101,1,1,11.0
Ground, SlopeDashing0x2A3,3,2,3,3,2,3,22.625
Ground, SlopeSword out0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Ground, SlopeCarrying object0x142,1,1,11.25
StairsCardinal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsDiagonal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsDashing0x0A1,1,0,1,1,0,1,00.625
StairsSword out, Cardinal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsSword out, Diagonal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsCarrying object, Cardinal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsCarrying object, Diagonal0x0B1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,00.6875
StairsSuperspeed, Cardinal0x0A1,1,0,1,1,0,1,00.625
StairsSuperspeed, Diagonal0x182,1,2,11.5
StairsSword climb, Cardinal0x142,1,1,11.25
StairsSword climb, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
StairsBomb climb, Cardinal0x142,1,1,11.25
StairsBomb climb, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Shallow water/GrassCardinal0x142,1,1,11.25
Shallow water/GrassDiagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Shallow water/GrassDashing0x303,3,3,33.0
Shallow water/GrassSword out, Cardinal0x101,1,1,11.0
Shallow water/GrassSword out, Diagonal0x081,0,1,00.5
Shallow water/GrassCarrying object, Cardinal0x142,1,1,11.25
Shallow water/GrassCarrying object, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Shallow water/GrassSuperspeed, Cardinal0x303,3,3,33.0
Shallow water/GrassSuperspeed, Diagonal0x182,1,2,11.5
Old man follower, GroundCardinal0x142,1,1,11.25
Old man follower, GroundDiagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Old man follower, GroundDashing0x142,1,1,11.25
Old man follower, GroundSword out, Cardinal0x142,1,1,11.25
Old man follower, GroundSword out, Diagonal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Old man follower, SlopeCardinal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Old man follower, SlopeDashing0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125
Old man follower, SlopeSword out, Cardinal0x0D1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,00.8125

The Math

When updating Link's coordinates, the game uses the following process:

  1. If Link is walking left or up, the speed is "reversed" (0x18 becomes 0xE8). Or in another sense, moving left and up are subtractive operations (pixels are removed from Link's X and/or Y positions), while moving down or right are additive ones.
  2. The most significant digit (1 in the case of walking on normal ground) is added to the coordinate directly.
  3. The least significant digit (8 in the case of walking on normal ground) is shifted left 4 times. 8 becomes 0x80 and added to the current subpixel value. If this causes the subpixel to overflow/wrap, another pixel is added to the coordinate.

So for example, a speed value of 16 (or 0x10 in hexadecimal) represents a clean 1 pixel per frame. 17 (0x11 in hexadecimal) would be 1 pixel per frame, with an additional pixel being added every 16th frame.

Some other examples:

Walking on normal ground

An example where Link's movement speed is 0x18 (walking cardinally on normal ground).

  • Downwards
    • frame 1: Subpixel = 0x00 + 0x80 = 0x80. Coordinate = 0 + 1 + 0 = 1
    • frame 2: Subpixel = 0x80 + 0x80 = 0x00. Coordinate = 1 + 1 + 1 = 3 (subpixel overflow)
    • frame 3: Subpixel = 0x00 + 0x80 = 0x80. Coordinate = 3 + 1 + 0 = 4
    • frame 4: Subpixel = 0x80 + 0x80 = 0x00. Coordinate = 4 + 1 + 1 = 6 (subpixel overflow)
  • Upwards
    • frame 1: Subpixel = 0x00 - 0x80 = 0x80. Coordinate = 0 + 1 + 1 = 2 (subpixel overflow)
    • frame 2: Subpixel = 0x80 - 0x80 = 0x00. Coordinate = 2 + 1 + 0 = 3
    • etc.

Walking on stairs

An example where Link's movement speed is 0x0B (walking cardinally on stairs).

  • Downwards
    • frame 1: Subpixel = 0x00 + 0xB0 = 0xB0. Coordinate = 0 + 0 + 0 = 0
    • frame 2: Subpixel = 0xB0 + 0xB0 = 0x60. Coordinate = 0 + 0 + 1 = 1 (subpixel overflow)
    • frame 3: Subpixel = 0x60 + 0xB0 = 0x10. Coordinate = 1 + 0 + 1 = 3 (subpixel overflow)
    • frame 4: Subpixel = 0x10 + 0xB0 = 0xC0. Coordinate = 2 + 0 + 0 = 2
  • Upwards
    • frame 1: Subpixel = 0x00 - 0xB0 = 0x50. Coordinate = 0 + 0 + 1 = 1 (subpixel overflow)
    • frame 2: Subpixel = 0x50 - 0xB0 = 0xA0. Coordinate = 1 + 0 + 1 = 2 (subpixel overflow)
    • frame 3: Subpixel = 0xA0 - 0xB0 = 0xF0. Coordinate = 2 + 0 + 1 = 3 (subpixel overflow)
    • frame 4: Subpixel = 0xF0 - 0xB0 = 0x40. Coordinate = 3 + 0 + 0 = 3

Resetting Link's Subpixel Velocity

The game will reset Link's subpixel whenever you change direction of movement. A movement after a standstill also counts as changing directions. The game only look at your dpad to determine the directional change. So if you're holding to walk upwards with a wall to Link's right, and then press , it will treat this as a change of direction, even though Link is still moving upwards. This means pumping into a wall will function as two "pumps", both when you press to and when you release back to .

Starting a dash will not reset the subpixel. But if you cancel the dash with a movement, it will be reset. If you start a dash that transitions to another screen, the subpixel is not reset unless you start moving in a different direction on the next screen.

Some items reset your subpixel and others do not.

  • Ones that don't: sword, boomerang, hammer, fire and ice rods, hookshot, bombs
  • Ones that do: medallions, magic powder

Subpixel's Effect on Slope Dashes

Dashing on a slope is a fairly volatile thing when you frame advance through it. Link will move anywhere from 0 to 3 pixels in both the horizontal or vertical directions, seemingly at random. And you will get random 1px/frame in only one direction or sometimes 0px/frame in both directions. None of this is actually random, but the game's algorithm for moving Link while he's against slopes isn't completely understood.

One cause for the slowdowns seems to be the same mechanic as "corner nudging". The game sees Link needs to be better aligned (too close to the wall in one direction) and moves him 1px in one direction away from the wall. This seems to happen less often if your subpixels for both axes are the same, and you are moving in either a "pumping direction" or "antipumping direction" for both axes (so upleft or downright). The main reason for this is that Link's coordinates will update equally in both directions since the subpixel velocity will overflow/wrap at the same time throughout the length of the dash. So when the game auto-corrects Link's position, the dashing pattern doesn't interfere with it afterwards.

Another cause for slowdowns seems to happen when Link hits the very first pixel of an 8x8 corner tile (when his hex coordinates end with 0 or 8). This can cause a 1px movement or sometimes a 0px movement.

What is certain is that the subpixel you have when a dash is started will affect how the slope dash ends up working out. You can get pixel boosts in either direction depending on what the subpixels are when you hit the corner, which would alter the path Link takes through the slope. So even if you start your dash from the exact same coordinate, the slope dash may proceed quickly or slowly depending on what Link's subpixel value is before the dash. One way to make sure you get the same subpixel every time is to hit diagonal before starting the dash (Link actually needs to walk diagonally as well). This is because:

  1. You get a change in direction, resetting the subpixels.
  2. Diagonal movement is a clean 1px/frame, so the subpixel will remain at 0.

Bomb clips

Since a criteria for performing a bomb clip is that Link is positioned exactly below the explosion, Link's subpixel value will matter just as much as your coordinate when you start the slope dash, since different paths through the slope might or might not include the coordinate you need to visit.

Some setups works the same for all subpixels, in which case only the coordinate matters.

Swimming

The below are mostly observations from frame advancing in emulator and may not be 100% accurate.

  • Swimming is 100% acceleration based.
  • The acceleration seems to be around 1/32 pixels per frame. So after 4 frames of holding right on the dpad, you will have the speed 1/8 pixels per frame (or 0.125 pixels per frame).
  • Deceleration seems to be around 3/64 pixels per frame.
  • The acceleration does not change by pressing A / B / Y. Instead, you get a higher maximum speed.
  • Max speeds:
    • D-pad movement, Cardinal: ~0.9px/frame
    • D-pad movement, Diagonal: ~0.65px/frame
    • Swimming with A / B / Y, Cardinal: 1.5px/frame
    • Swimming with A / B / Y, Diagonal: 1.0px/frame
  • Since pressing A / B / Y doesn't change how fast you get to 0.9px/frame, you should wait until you've hit the normal max speed (0.9px/frame) before starting to mash the swim buttons. That way, you will stay longer at the higher max speed before having to swim again. Worst case (pressing A right after pressing the dpad from a standstill) you only spend one frame at the 1.5px/frame speed. It takes around 31 frames from a standstill to reach 0.9px/frame.
  • After reaching the absolute max speed (1.5px/frame) and the swim starts decelerating back to 0.9px/frame, repressing A / B / Y works strangely. If you press it at 23/16 pixels per frame, you get back up to 24/16 (or 1.5px/frame). But if you press it at e.g. 21/16 pixels per frame, the next max speed will be 22/16 (1.375px/frame). Basically it rounds up to the next even sixteenth. Worse case, if you let Link decelerate for ~12 frames before repressing A / B / Y, you will be stuck with a max speed of ~1px/frame for a while, until it decelerates down to 0.9px/frame. So there's multiple reasons to mash as fast as you can when swimming (or time it very well).

Last updated September 15, 2026 by joshRTA