Mario Maker 2 Ninjis

Created Monday, May 6, 2024

In another offshoot of my Mario Maker 2 Datasets project, which was itself an offshoot of the Mario Maker 2 API, I have been using the ninji data I scraped to render visualizations of each speedrun and the performance of the players. This blogpost is about the first part of that exploration: rendering speedruns as a trajectory visualization.

link Ninjis From The Server

As an initial start I counted the number of replays for each ninji event, each one obtained from the dataset and by using a call to The numbers are as follows:

  • "Rolling Snowballs": 365366
  • "The Speedventure of Link": 204849
  • "The 10-Coin of Deep Woods": 209097
  • "Cat Mario Dash": 202793
  • "Banzai Bill Cliff Climb": 168703
  • "Swinging Claw Flyway": 189040
  • "Headgear Hustle": 218865
  • "Balloon Race": 220576
  • "Yoshi’s Piranha Plant Picnic": 150860
  • "Player’s Choice: Power-Up Party": 140888
  • "Big Shoes Gustin' in the Desert": 108323
  • "Squirrely Airship Escapades": 121491
  • "At the Croak of Midnight": 69813
  • "35th Anniversary Auto-Mario": 133055
  • "Cannon Box Blast!": 78050
  • "Goombud Bust-Up": 100664
  • "SMB2 Mario: Can You Dig It?": 93664
  • "Dry Bones Shellscape": 84862
  • "Cape Mario Master": 52811
  • "Bowser’s Castle: The Last Dash": 101019
  • "Link’s Lightweight Longshots": 73792

Number of players in each Ninji event

link Parsing the File Format

In order to render the ninji replays as they were shown in game I needed to reverse engineer the binary sent to the client. One initial question I had was whether this binary contained inputs, which would be played back by the game with accurate physics for every replay rendered, or positions, which would simply render the replay in a specific position on each frame.

As a base I started with Kinnay’s reverse engineering work. This made it clear that ninji replays contained positions. (notably, there is a place in SMM2 where input replays are recorded, I’ll be making a blogpost about that in the future). With some further research and testing I modified the format to the following:

0x00x3CFile header
0x3C0x249FAReplay frames

link File Header

Big endian.

0x04Version number (always 2)
0x84Unknown (usually 64)
0xC4Time in milliseconds
0x104Number of frames
0x151Unknown (usually 1)
0x162Unknown (usually 4)
0x1C4Unknown (maybe points?)
0x204Unknown (usually 0)
0x244Unknown (usually 0)
0x284Unknown (usually 0)
0x2C4Unknown (usually 0)
0x304Unknown (usually 0)
0x344Unknown (usually 0)
0x384Magic number (always SPGD)

link Replay Frame

Corresponds to every 4 frames. Little endian with no padding.

A: Flags
B: Player state
0x12X position (tiles are 16x16, centered on tile)
0x32Y position (tiles are 16x16, centered on tile)

If flags & 6:


If Unk1 & 24:


link Flags

A bitmask.

x & 0100In pipe transition
x & 1000In subworld

link Character


link Player state

link All

3Ground pound
5Slide down slope
7Dry bones shell
8Clown car

link SMB1

4Link suit downward sword

link SMW

12Cape feather glide
13P balloon moving
14P balloon stationary

link NSMBU

4Slide down slope
6Slide down wall
12Acorn suit glide
13Propeller suit fly
14Propeller suit glide

link SM3DW

4Slide down slope
6Slide down wall
7In pipe
8Cat suit dive
12Bullet bill mask
13Propeller box fly
14Propeller box glide

link Researching Visualizations

Next I started writing code to parse the replays and render them. I used C++ and Skia, as the number of replays, each with possibly 300 or more replay frames, would strain any other rendering library. Skia was designed to render webpages in Chrome by Google, so it’s extremely fast.

A good visualization is one that can encompass the knowledge of the entire event in one picture. That is how I came across a trajectory visualization, which is often used in movement data.

I applied a exponential decay curve to the colors of each replay, as it is guaranteed to be 0 at 0 and 1 at 1. This allowed me to use a color for the fastest time, which is lime green, and the slowest, which is red. The top 10 runs were colored aqua blue to set them apart.

The equation is as follows:

\(HSV = \left(15 + (1 - p)^{\frac{1}{d} - 1} \times 0.95, 1, 0.75\right)\)

Where p is linearly 0 for the fastest time and 1 for the slowest. d is a chosen value, tuned to show the differences in times best.

  • "Rolling Snowballs": 0.001
  • "The Speedventure of Link": 0.03
  • "The 10-Coin of Deep Woods": 0.03
  • "Cat Mario Dash": 0.04
  • "Banzai Bill Cliff Climb": 0.03
  • "Swinging Claw Flyway": 0.01
  • "Headgear Hustle": 0.03
  • "Balloon Race": 0.02
  • "Yoshi’s Piranha Plant Picnic": 0.01
  • "Player’s Choice: Power-Up Party": 0.04
  • "Big Shoes Gustin' in the Desert": 0.05
  • "Squirrely Airship Escapades": 0.02
  • "At the Croak of Midnight": 0.05
  • "35th Anniversary Auto-Mario": 0.03
  • "Cannon Box Blast!": 0.07
  • "Goombud Bust-Up": 0.05
  • "SMB2 Mario: Can You Dig It?": 0.04
  • "Dry Bones Shellscape": 0.03
  • "Cape Mario Master": 0.03
  • "Bowser’s Castle: The Last Dash": 0.04
  • "Link’s Lightweight Longshots": 0.09

For example, for "The Speedventure of Link" the color curve looks like this:

Desmos color curve

This curve highly punishes even remotely slow players. This is because in practice there will always be players who are as slow as possible, thus the players that represent those attempting to improve their runs significantly usually constitute only the top 10% of runs.

Next, I started rendering lines. Because ninji replay frames happen only ever 4 render frames, or around every 66 milliseconds, the resulting replays look rather polygonal when rendered point to point. Because of the lack of information present in the replay this is an unavoidable fact. I chose not to render the replays with splines and instead just rendered pixel-perfect lines between the positions.

link Trajectory Visualizations

Click to zoom into each visualization!

Rolling SnowballsThe Speedventure of Link
The 10-Coin of Deep WoodsCat Mario Dash
Banzai Bill Cliff ClimbSwinging Claw Flyway
Headgear HustleBalloon Race
Yoshi’s Piranha Plant PicnicPlayer’s Choice: Power-Up Party
Big Shoes Gustin' in the DesertSquirrely Airship Escapades
At the Croak of Midnight35th Anniversary Auto-Mario
Cannon Box Blast!Goombud Bust-Up
SMB2 Mario: Can You Dig It?Dry Bones Shellscape
Cape Mario MasterBowser’s Castle: The Last Dash
Link’s Lightweight Longshots

link Questions?

Use the Contact button to the side or join my Discord.