Hi, I co-run No Goblin with Panzer and our wonderful team. Sometimes I stream weird games too.


hecate
@hecate

this post assumes some familiarity with the game Neon White, such as the general flow of picking up cards, shooting them, and discarding them for various movement abilities. Familiarity with things like vectors in 3d space will also be handy, but hopefully not required. I'm not really sure if there's an 'appropriate' level of familiarity for this post, because gods knows where it's going. If you haven't seen it already, you might want to just watch the recent GDQ run, where you'll see some bits and pieces of this fly by!

there's a lot of systems at play and things to optimize for in Neon White, so I figured it'd be worth collating together a bunch of the finer details that are all helpful for playing the game well at a high level. i'm not going to be going over things like routing lines or anything, but moreso the details of the mechanics and how they interact. I've edited most of this information into the community tech document by this point, but that lacks a lot of the deeper details that I want to write about / hopefully my prose glueing things together here paints a better picture of the systems at play, so that understanding the game and breaking it accordingly is easier for anyone who wishes to do so! (and also it's totally possible there's some random bullshit detail I forgot to ever put into the tech doc that I'll throw out somewhere in here)

🌕 We're going to have some additional blurbs with bonus technical details sprinkled throughout this post. We've spent a lot of time reverse engineering the game & poking at precisely patching it that we're going to do our best to separate out the practical implications of tech from the technical implementations a layer deeper. This is my best attempt at compromising between breadth and depth on this writeup, since I could go *deep* on the technical spaghetti here at this point


Before we dig in to the technical bits, some background info and tidbits permeating the game itself. It's a very speedrunny arena game: levels are always fixed, with unchanging enemy positions (none of the enemies move, except for uhh, jumpers! and stuff in the boss levels, kinda!); there's no RNG or cycles (until you get into frame ordering making shocker+fireball boosts very inconsistent/framerate dependent, or the invisible crystal spinner in The Third Temple fight); the cards are all just a weapon + a movement discard (except for Dominion and BooF). There's the in-game leaderboards, but there's no run validation or deterministic ghost files that can be used for run verification.

As one of top runners said at one point, "it's a speedrunning themed game, but not a great speedrunner game". I don't actually agree with the latter part here (doing full-game runs isn't ideal - save management etc is nonexistent - but IL grinding and level rush grinding is super streamlined, and it led to very fast community progression of WRs in both ILs and the 'all level' rushes/etc). If it wasn't a great speedrunning game, people would actually entirely stick to the in-game leaderboards (which are overrun with cheaters at this point due to dev inaction for a variety of reasons I'm not unpacking here) rather than seek out the speedrunning community, have shadowplay/replay buffers/recordings of WRs, etc.

It's also still a video game, and as such, it has its own jank and tech and bugs that we can lean on to do things Faster. So, let's dig into all the systems at play here.

Game ticks vs frames and some Unity bullshit

There's a small bit underlying everything in the game: the underlying engine is Unity, so there's a few engine fundamentals. There's two different ticking methods every object has available: the general per-frame .Update() tick, and the fixed-tickrate .FixedUpdate() tick. There's also subroutines that have different execution timelines, but we'll get to that later when we get to the Shockers and their jank.

🌾There's also a .PostUpdate() (or something, I forget the name) that's called after .Update has ticked for all objects - a second step to the frame-update ticks for anything that needs to run in a second stage. However, none of the game logic really uses this, at least not in any meaningful way that I can remember, so the second stage of frame updates aren't in consideration here

The impact of frames vs ticks is a bit weird - game velocity stepping is done on ticks, while position stepping is done on frames. Some input polling (movement keys + jump) are done on ticks, while others (mouse inputs, card swapping) are done on frames. This tends to shake out in different ways depending on the systems involved, but the gist is that high framerates are not necessarily better in Neon White. While they certainly feel better due to mouse input polling every frame (which makes shooting/using cards feel more responsive/fire as soon as possible), there are some bits of tech that become harder or impossible if there are any (or are too many) frame updates between tick updates.

🌾 additionally, when doing TASes for this game, you *must* record at 60Hz vsync so that frametimes are tied to the tickrate; 60hz fps limit with no vsync apparently produces more varied frametimes than with vsync, which leads to occasional desyncs just from hiccups in frametimes leading to mild position stepping misalignments. This is also, hilariously, the reason why 'ghosts' in Neon White are just snapshots of positions at given timestamps as opposed to inputs files or anything

🌾The split between input polling is due to a cute boundary between classes in the game logic: there are two classes that serve as the boundary between the player and the first-person side of the game.

FirstPersonDrifter is the primary one, and it derives from the ICharacterController kinematic controller interface. In addition to handling velocity adjustment from sloped collisions and the like, the interface invokes UpdateVelocity on FirstPersonDrifter on every fixed tick update (60 times a second), and that then steps the velocity for the next 60th of a second, taking into account the current WASD keys that are held, if space was pressed or let go, and other movement-related conditions. There's also some amount of logic done on the frame updates - the actual input polling is done on frames, and the results are just stashed into some private vars for the next tick-update to use, entirely just so some weird input buffering around holding and releasing jump works nicely.

MechController is the secondary class involved here, and is responsible for handling polling for all the non-movement inputs - quick-restarting, discarding, shooting, and swapping. It's also the class that's the interface for other parts of the game to do non-movement-related things to the player (such as health, directly giving cards, and triggering splash damage effects at the end of movement effects.

Input stuff

If you hold jump, you always jump on the first frame possible - you will bounce right off the ground and you cannot do better by mashing or anything. Whoo!

You cannot hold jump on the first tick of a level unless you also have a mouse button bound to jump - since the jump key is used to start a level, there's some debouncing involved, unless you also hold your mouse button jump through it all.

Due to some jank involved in the coyote jump timer and unity input polling internals, you also want a mouse button to jump with for coyote jumps - you can trigger them much more reliably when walking off ledges by using your mouse buttons instead.

🌾 We could never figure out why this one is how it is besides "there must be some difference between how unity polls for mouse inputs, including mouse buttons, and key inputs, and that manifests with this weird frame/tick timer quirk?". Some depths are better left unexplored, frankly.

Velocity fundamentals

Every tick, the kinematics controller invokes .UpdateVelocity() on FirstpersonDrifter, which returns the overall velocity up to the kinematics controller to do things with (notably, it adjusts lateral momentum on sloped surfaces, making it faster to jump up over slopes vs walking up, generally). There's a few major velocity components that get calculated and smashed together before the final velocity value is emitted, but these components are also externally adjusted frequently between ticks by cards and the like, so understanding the difference is a bit handy.

The first velocity component is your movementVelocity; this is generally your velocity from your current movement mode (e.g. the intrinsic velocity that WASD apply to you, godspeed's initial 0.5s dash, movement while ziplining with a Dominion, etc). There's not much you can do here, but one highlight is that it takes two ticks to reach your maximum lateral velocity from just holding W - your maximum lateral velocity is 18.75u/s, but you only reach like 18.0u/s in one tick or something silly. Otherwise, it's just generally Basic Bitch Velocity that is set in very straightforward ways.

The other velocity (literally just 'velocity' internally) is what we call 'bonus' velocity. This is where your bullet boost momentum, water boost momentum, godspeed exit velocity, and the like all goes, and this velocity component degrades down to 0. There is a lot of different momentum that can go into your 'bonus' momentum pool that you can opt to micromanage.

🌾for example, due to a quirk in how the `.AddVelocity` method works, it clobbers your '.movementVelocity` to 0 until it's re-calculated on the next tick. Bullet boosts and parry boosts take your current movement direction - the normalized direction of your averaged wasd inputs on an x/y plane (e.g. -1 to +1 in x/y; holding W+D would be (sqrt(2)/2, sqrt(2)/2)) - and amplify it, and put that bonus momentum into the bonus velocity component.

However, AddVelocity just.... always zeroes out your MoveDirection, other calculations using it until the next tick will not work. This generally just shakes out to "you can only bullet boost once per tick" - which has practical implications when it comes to Ringers! - but other sources of bonus velocity, such as water boosting or explosions, are not affected by this since they don't rely on reading your current MoveDirection.

The 'bonus' velocity degrades more slowly in the air. I am not digging into the finer details of this velocity degredation-stepping:

code snippet: screenshot of some decompiled c#, text at gist.github.com/PandorasFox/e31fb4edf05c41e8e44ff79e964cbcb2

The important bit here is just "when you're grounded you have different friction applied, and it ramps down more quickly at small values". It degrades more slowly in the air, letting you hold momentum from Godspeeds and the like for longer. The specifics of how the momentum decays doesn't matter nearly as much as the broad difference that translates to "hold onto momentum in the air".

This is also why jumping against low ceilings/hovering over water is faster than just running on water - the waterboost effect is actually just Bonus Velocity given continually as long as you're in the water boost trigger zones (which extends just a bit over the water). The maximum grounded waterboost velocity of 33.75u/s is actually just the equilibrium between the momentum degredation and the continual boost effect stacking up; as soon as you jump into the air you start to build even more momentum - but unless you can hit a ceiling and stay within the water boost trigger, this ends up being a loss; it's mostly used in a couple tunnels and on upward-sloped water ramps.

As a last note on velocities, I'm just dropping a quick list of the important velocity numbers in the game. I pulled all these numbers out of the game at some point and put them in one spot in the speedrunning discord, but I'd like a lot of this info to.... well, live outside of discord.

Upwards velocity components:

  • Stomp: -70u/s
  • Terminal gravity: -22.75u/s (or thereabouts)
  • shocker: 15u/s
  • Jump: 22u/s
  • Balloon (from top) ~25u/s
  • Elevate, Boof: 30u/s
  • Balloon (from bottom) ~35u/s
  • Purify: 41.25u/s
  • Explosive barrel: 44u/s

Lateral Velocity components:

  • Air bullet boost: 7.2u/s
  • Grounded bullet boost: 14.4u/s
  • Godspeed exit velocity: 20u/s
  • Purify, Dominion (max, at eye height): 22.6875
  • Explosive Barrel (max): 24.2
  • Boof: up to 50u/s (!) if parallel to the ground w/ target
  • Shocker: 70u/s

Other velocity numbers:

  • Max unboosted ground/air speed: 18.75u/s
  • max grounded water speed: 33.75u/s
  • Dominion grapple: 35u/s in a straight line
  • Max aerial water-boosted speed: ??? honestly dunno

Cards

There are 9 notable cards for our purposes:

  • Fists
  • Katana
  • Elevate
  • Purify
  • Godspeed
  • Stomp
  • Fireball
  • Dominion
  • Boof
🌾 The miracle katana doesn't have much going on aside from the bug that lets you discard it twice and end up in a broken state. (Which sounds like a lot, but really, you just end up with a `null` card in your hand and it makes the MechController class behave a bit weird. It's only usable in Hell Rushes, and has no actual implications because it's frame-dependent, can take a while to pull off, and generally means you just pick up ammo for cards every frame at the cost of being unable to discard anymore. The inability to discard limits this to only being meaningful on Dominion levels where you might just want more ammo fast to do silly rocket jumps, but the inability to discard is simply too big of a price for that bug to be useful or notable. so, it's reduced to this interjection i guess. which has ended up being a lot more of an explanation of why it's pointless than an explanation of the actual quirk there. oh well!

Fists

a picture of the Fists card. it is grey with a fist silhouette on it.

The fists are notable in that they let you kill yourself. If you use all 30 katana ammo, you're given the fists (with 10 ammo); if you use all 10 fists ammo, you die. As it turns out, dying is actually useful! On the three boss levels, the boss has a slow death animation that the timer runs during after your last action. However, if you kill yourself by way of punching the air with your last fists ammo right after your last action, you can sit on the death screen until the level completes, stopping the timer early. Aside from that, the fists Just Suck - they are strictly worse than the katana for parrying/hitting enemies/etc.

Katana

a picture of the Miracle Katana. it is grey with a golden katana silhouette on it

The katana is only really interesting as it's our primary way of parrying projectiles. We don't actually care about it when looking at all the cards. I'll get to parrying, bullet boosting, and phantom parrying later.

🌾 It actually is worth noting that the fists just have a straight hitscan check in a straight line, while the katana does 30 raycasts out in a horizontal pattern. I was too lazy to make the game actually render these raycasts, so instead I just shot bullets along each ray to visualize them instead.

Elevate

a picture of the Elevate card. it is yellow and has a silhouette of a basic pistol on it

Elevate seems like a simple card at first - it's a pistol, and it lets you double jump. But thanks to a secret mechanic that I call 'jump capping' (since that's more or less what the devs named the relevant variables), it's Kinda Weird Actually! Most upwards velocity in the game is applied via `FirstPersonDrifter.ForceJump()', but only the two jumps are 'capped'.

Detour: Jump Capping

'jumps' are applied in a pretty straightforward way - they cancel movement abilities (if they'er supposed to), subtract out the 'capped' jump cost if there is one, and then zeroes out the vertical momentum if it's negative after that cost subtraction. essentially, it's a fancy way to prevent you from burning an elevate right after hitting space to directly stack the velocities - we want it to feel like an extra jump, and not just a straight upwards velocity explosion. iirc only jumps & double jumps use this at all - so if you were to jump and then shoot an explosive barrel, you'd get roughly the same upwards velocity as just walking up to the barrel and shooting it. however, if you walk up and shoot the barrel and then immediately discard your elevate, you'd end up with ~74u/s peak upwards velocity, since the upwards barrel velocity is applied as an 'uncapped' jump and clears out any lingering jump caps from your last jump.

it's a bit weird that the jump caps from things linger, but since all (iirc) upwards velocity is applied through this method, and it floors it to zero between subtracting the capped cost and actually adding the upwards 'jump' momentum, it ends up not having an effect on much, since things end up working pretty intuitively.

Purify

a screenshot of the Purify card. it is purple with a silhouette of an assault rifle on it

Purify's grenades are parryable. You can shoot them repeatedly for rapid bulletboosts as you approach them, or you can just katana them quickly before they explode. They're the only projectile that you can make and then feasibly do this boost off of. It's a really silly micro optimization.

The other important knowledge with Purify is how explosion velocity is applied to the player.

Detour: Explosions

(all of this applies to purify bombs, dominion rockets, and explosive barrels).

Explosion knockback is calculated in a straightforward way: the game takes the vector from the center of the explosion to your camera point (so, yknow, player head/eye height), normalizes it [fixes it to a uniform length of 1, but direction unchanged], and then removes the vertical component to cast the knockback onto the lateral plane. It then takes that vector and just multiplies it by the explosion force, and that's the lateral knockback. Vertical knockback is always a constant upwards force, regardless of if the explosive source is above/below you.

🦊 The important takeaway here is that between an explosive source at your feet, and an explosive source at head height, the one at your head height will provide more lateral knockback to you! The knockback vector at head height is 1 unit in a purely lateral dimension, while the knockback vector at a 45-degree upward angle ends up only being about 0.707 units long on the lateral plane before having the explosion force applied. In general, you want explosions to be at head height to maximize your lateral velocity gains.

The other thing underlying all explosion forces is that all the knockback values applied are independent of distance from the explosion source, as long as you're within the explosion radius. That's not to say that explosion direction doesn't matter, but if the knockback vector is exactly the same, there's no difference between the explosion being 1cm or 10cm away..... UNLESS you are in the outer 1% of the explosion radius! in which case the explosion knockback force tapers off from 100% knockback force at the 99th outer percentile of the radius, to 0% knockback at the exact edge of the explosion radius.

So generally, explosions at head height, and just make sure you're within the inner 99% of the explosion radius. The outer 1% is incredibly finnicky and precise, and hard to use in speedrunning without setups and/or patience in ILs.

Godspeed

A picture of the godspeed card. it is a deep blue with a silhouette of a bolt-action rifle.

relatively straightforward weapon: when you discard it, you dash forward with an initial dash of speed for 0.5s, and then have an exit velocity applied as bonus velocity. Due to how bonus velocity lingers for longer, you want to godspeed in the air to hold onto that exit velocity for longer.

Godspeed locks you to the lateral plane for its dash.

Fireball

A picture of the fireball card. it is a crimson red with a silhouette of a shotgun on it.

Fireball is more or less an inverse Godspeed - instead of locking you to the lateral plane and giving you a lot of lateral exit velocity, fireball does the same initial dash - however, the initial dash velocity is not locked to the lateral plane and instead can also go up and down. The exit velocity is tamped down in the lateral dimension, but doubled along the vertical axis. It also gives you the water boost effect from being on the ground for the full duration of the particle effects around your screen - this does not stack with running on actual water, but it does mean that you move hella fast during a fireball and can jump at the very end to carry as much of that velocity into the air as possible.

So, yeah, inverse godspeed. Use it on the ground and make good use of the waterboost effect, and use it for vertical traversal.

Stomp

a picture of the stomp card. it is a deep green with a silhouette of an uzi on it

Stomp is blessedly straightforward: you right click it, and you drop straight down at 70u/s until you hit something or use another card to cancel it.

if you land on a breakable object (such as an enemy's head, or a red breakable surface thing), you get a small bit of initial downward velocity smashing through it, rather than getting reset to 0 downward velocity from landing on them before they die.

There is a fun interaction from cancelling stomp with an elevate, though: due to Shenanigans, the stomp movement mode gets cancelled, but the -70u/s velocity isn't cleared, specifically only in the Elevate ability call path - so you end up with -40u/s (-70u/s + 30u/s from the elevate), swing back to -70u/s, and then accelerate past that as gravity applies itself to the other momentum component. Last I checked this wasn't used in any ILs though, since there's nowhere where we have both a stomp and an elevate and can benefit from the minor amount of lateral movement / can fall for long enough to make use of the higher peak negative speed.

I stand corrected! Thank you xiamul for correcting me here - this is actually used in the Jumper WR now:

Dominion

a picture of the dominion card. it is a deep cyan, with the silhouette of a rocket launcher and a grappling hook on it

The rockets don't have any tech beyond what was outlined in the Explosions section for purify bombs - shoot walls at head height and be aware that they always pop you upwards/glue you to ceilings, but that's about it there.

Zipline though - it just pulls you in a straight line towards whatever you were looking at. Wonderful for moving in straight lines. You can exit it early by jumping; this does not give any additional lateral velocity all, but does give a bit of upwards velocity. Zipline can also do a small amount of damage (iirc 2 damage, dealt as splash damage?) to enemies, but generally that's just used for breaking glass panels instead.

BooF

A screenshot of the book of life card in-hand, with the fists next to it. it is a very ornate white-and-gold card.

The Book of Life (called the BooF because of the funny font on the card, and also because saying 'boof' is funnier) is the telefrag card and is shockingly straightforward. It's very point-and-click - most of the tech with the card revolves around tripwires not killing you if you telefrag onto them from far enough away. It also has a max range of 300 units (500 units? idr), which is Considerable but also very mid-length in the scope of the size of BooF levels.

Telefragging does give you some exit velocity as bonus velocity, to give you the air bounce and rush of speed popping off your target. However, since BooF jumps tend to be reasonably long distances and relatively fixed lines, the exit velocities involved here tend to be mostly route-specific and generally is a 'vibes' mechanic for making the movement feel really fucking good, but isn't terribly notable in speedrunning.

In general the boof boils down to being about routing rather than technical knowledge, since it's a telefrag card. The noteworthy bit is just that you can telefrag through clipping gaps in level geometry; there's several places this is done in ILs and rushes.

🦉 Shortly after the game came out, the devs actually wanted to patch out the bug with the BooF that let you telefrag onto tripwire chains and completely break levels.

For example, Marathon, the last non-special level in the game, is supposed to be a really nice minute-long trip through little segments themed around the different cards of the game, where at the end of each segment you break a tripwire, which breaks a chain of tripwires and ends up breaking down a series of thick walls so you can get to the end of the level. However, in BooF levels, you do not actually need to kill all the enemies like usual - you just need to get to telefrag onto the book after getting close to it (usually by telefragging onto an enemy nearby).

Once it was discovered that you could avoid dying when telefragging a distant tripwire in the middle of a chain (directly into the path of another tripwire, which is supposed to immediately kill you) due to the tripwires uh.... not being active at a distance? or just weird stuff with where it positioned you post-telefrag + high velocity => you could phase through the tripwires? i think it's just bc it positions you a bit above the thing you telefragged since normally you 'bounce' off them), people uh, did this to marathon and now it's doable in like, ~1/5 of the time, and is generally way less fatiguing of a level to do - no one wanted to run the ~80 second level, definitely not at the end of a long run, so the telefragging was much preferable even if much riskier.

The devs wanted to fix this in the first patch and put out a beta branch with it fixed. The backlash was immediate: no one wanted speedrun tech to be removed from a speedrunner-y game. No one wanted to enter into patch hell where it was faster to run a game on 1.0 rather than a later version. No one wanted to deal with resetting the leaderboards for the affected levels. So after what I assume was a nonzero amount of harassment from Gamers, the devs dropped that (there were some other things too - Violet's last sidequest level had an issue where a tripwire on one side of the last room was aligned wrong and let you just walk past the tripwires instead of doing the actual level. The devs ended up fixing that tripwire, but secretly added another way to just walk past the lasers at the end and continue skipping the whole level - and their new way was slightly faster than the old route, so it also avoided needing to mess with leaderboard resetting or whatever.)

Both of those were cases of the devs wanting to Fix Bugs and make the game fit their idea of perfect, and I'm kinda glad that they didn't fix the boof tripwire bug, but also am kinda sad that it means Marathon, for as beautiful of a level as it is casually, is just kinda this "lol 10 seconds of clicking" level in ILs.

Anyways, BooF basically boils down to telefragging through tripwires, or just clipping through geometry.

Enemies

Most of the enemies (imps, frogs, jocks, guardians, tripwires, and bubbles) are not terribly noteworthy. Imps, frogs, and jocks sit in place and throw their projectiles. Guardians simply kill you as their only interaction. Tripwires and bubbles have interesting routing implications in their levels, but mechanically are... tripwires, and bubbles that you must pass through in order to do a level (so effectively, required checkpoints to pass through - they're interesting as routing constraints!)

Balloons, ringers, shockers, and mimics do have interesting notes, though!

Balloons

a picture of an enemy balloon. it's a black orb with a demon tail hanging down below it!

Balloons give slightly more upwards velocity if you hit them on the underside, and slightly less if you hit them above their center point. Mostly notable on a few levels where you want to micro-optimize for exit velocity coming out of a balloon sequence, or for when you want to skip a balloon in a sequence.

Ringers

a blob with a bunch of eyes on it. It attacks by shooting out a lateral ring of projectiles around it, hence the name 'ringer'

Ringers put out absolute fuckloads of projectiles and are noteworthy in that they (alongside Jumpers) are the only way you can really be exposed to the 'you can only bullet boost once per tick' limitation - you can manage to shoot their projectiles with, say, a fireball, and sometimes end up with back-to-back ticks of projectiles being broken and boosted off of (or just all one tick if you're unlucky), or rapid fire with a purify/stomp and get them rapidly.

Jumpers

a jumper (green shadow with a funny hat, paw-lookin feet, one bat-wing, and a staff with a crescent moon on the end) staring at the player character with a ? over its head

the same jumper, a few frames later, jumping and attacking

Aside from being the only other multi-projectile enemy, Jumpers have a silly little bit of enemy AI: they jump when they aggro on you! as soon as they enter their attacking state, they do a jump. And as soon as they enter their 'reloading' state, they do a defensive jump!

🦊 When grinding at ILs in Neon White, you tend to fall into a bit of a rhythm with your attempts due to how there's no RNG or anything in most levels - everything is generally very deterministic, and you hopefully start getting down a good rhythm through the level as you get a bit of muscle memory for the different snaps and stuff you need to do.

Jumpers can be kinda frustrating though - if your lines are a bit different and you're seen a bit earlier or later, it can change when the Jumper does their aggro hop and make one of your shots miss or generally not kill them. I know that "well just think about the fact that the jumper jumps 4head" is not terribly handy advice but it does sorta boil down to that on a deeper level: if they give you trouble, would it be better to take a line that's a tenth of a second slower, but gets you a better angle on the jumper so they aggro hop at a more consistent time, letting you have more consistent runs?

There's just generally a lot to consider in how you play the game - do you only care about friend leaderboards, do you look at the global leaderboards, do you want consistency in Level Rushes over shaving off fractions of a second in ILs, do you just want the Ace or Dev medal, etc - and they're all factors in how you want to attack and approach a level. (I really struggle at breaking out of always doing IL strats - completing a White's Hell Rush without throwing it away doing something risky partway through took me a few hundred attempts!)

Shockers

a picture of a shocker on a stream of water. it looks like a weird hexagonal eyeball surrounded by 6 "leafs", one of which is green and is the 'intended' direction to hit for the generic path through the level

These fuckers have all the jank. I love them.

🌾 Their shock-leafs are purely visual and have no collision - while they are accurate for conveying the Shocker aggro zones, Shockers work by way of their AI ticking on every frame, and 'attacking' you with the boost if you are in one of the aggro quasi-cones. If you run below 60fps, you run the risk of Stomping through the aggro zone and hitting the ground (and killing) a shocker before it attacks you.

🌾Shockers also have a delay between "dying" and dying. Normally, when a shocker does its shock effect, it's supposed to die. However, this is handled by doing the death in a subroutine that only has the Shocker die after a `dieDelay` number of seconds..... which happens to be 0 seconds always! It also cancels and restarts the death subroutine if a boost is triggered again, after the death subroutine was started, but before the death subroutine actually runs and kills the shocker.

The death subroutine in shockers leads to a, well, shocking amount of weird tech and jank:

  • At or below 60fps, you are able to trigger multiple boosts in back-to-back ticks and stack the velocity impulses via fireball
🔥Colliding with a shocker when you have Fireball active kills a shocker via the same codepath as telefragging (not actually notable). It cancels your movement abilities & momentum like usual - it does properly terminate the Fireball momentum and applies the shocker impulse.

However, it does not terminate the fireball state.

Fireball is actually handled via a subroutine - when fireball is active, it does the dash, and starts a subroutine. The subroutine handles turning on the fireball particles around the edge of the screen, giving you the waterBoost if you're grounded on that subroutine tick, turning off the isDashing state after 0.5s, and then finally turning them all off after 4s when the subroutine ends.

However, the subroutine itself is never cancelled by the Shocker, and the subroutine being active is what determines your fireball state for whenever you are colliding with a Shocker. So every tick that you are colliding with a shocker, you immediately collide with it, get the momentum impulse from it, and then cancel the last death subroutine and start a new one. Apparently, due to a Unity quirk, subroutines do not start to execute until the next frame update - but at lower framerates, if we just don't have two frames between ticks, we can stack up to like 7 of the shocker boosts. Just one frame (or 0 frames) between ticks is fine iirc.

Hilariously, because this also stacks the vertical momentum, you cannot abuse this to get super ludicrous lateral momentum without also getting yeeted pretty far upwards. However, it's beneficial to do some parts of some levels at high framerates (to avoid accidentally triggering this and being sent to the stratosphere) or at ~75 fps (to get maybe a double boost relatively consistently). You can see this in the GDQ run at a couple points.

  • You can kill shockers twice in one tick and double-decrement the demon counter, letting you then skip killing another demon in your routing (currently only used in a couple IL TASes).

In general the shockers are just kinda weird enemies because of the boost stacking. Shocker levels tend to be pretty straightforward routing with little variance due to how consistent and fixed their boosts are - but the framerate jank leaves some room to accidentally shoot yourself in the foot (e.g. at the end of 6-6 shocker you have to fireball into the last shocker, and can easily send yourself flying over the goal and lose like 5 seconds waiting to just fall onto the goal).

Bullet Parrying and Phantom Boosts

Bullet parrying and bullet boosts (shooting projectiles out with your own projectiles) both apply the same parry-boost to the player and largely work the same. The main caveat with bullet parrying and boosting is that this boost can only be applied once per tick; if you can shoot out projectiles over time rather than parry them all at once, you can eke out more speed overall.

🦊 You can also parry / bullet boost off your own projectiles! Although most of your projectiles all travel at high speeds, there are two exceptions: purify bombs and rockets, both of which move slower than your bullets.

I don't think that shooting your own rockets out is generally done, partially because there's not many levels where you have a rocket launcher, also have other weapons, and also have rocket ammo to spare / time to line this stuff up. However, purify bombs have a lot going on - if you have multiple purifies to discard, you can shoot as soon as you yeet out one purify bomb, and stack bullet boosts on it as it flies away from you! And once the purify bomb collides with anything and sticks, you have 0.4s to beat the devil out of it and stack 5 bullet boosts from shooting your Purify right at it.

🌾 5 bullet boosts is off the top of my head here - I'm judging by the 0.08 fire rate on Purify [cell Q7 in this spreadsheet of the raw card values I made after pulling all the numbers out of the weapon assets.

All projectiles in the game also have a fun 'hit decay' state, where upon hitting anything (from another projectile, to an enemy, to any surface, etc) they invoke their AfterHitBehavior and do the following:

  • snapped to the point of impact
  • frozen in place
  • marked as 'not alive'
    • this just means that the projectile cannot be shot out with another projectile / cannot inflict damage from walking into where the projectile is frozen
  • marked as being in 'hit decay' mode
  • assigned a new temporary lifetime of either 0.25 or 1.25s, depending on who the initial damage target was.
    • If the 'initial target' (e.g. target before projectile was parried) was the player, the projectile has a temporary lifetime of 0.25s - so enemy projectiles will have a lifetime of 0.25s upon impact, even if you parried it. (If you parried a projectile and it also hit something, though, it is functionally entirely dead and useless).
    • If the initial target was "Damageables" (your own projectiles) or "DamageablesAndPlayer" (mimic projectiles!), the temporary lifetime is 1.25s. This is why mimic projectiles are parryable for what feels like an eternity after their projectiles hit a wall. You can sorta see this on this IL, where there's just a fuckload of mimic projectiles to trigger parryboosts with.

Now - the entire point of this hitDecay state is, as far as I can tell, just so that the player can parry the point of a bullet impact and receive a speedboost off of nothing. Specifically you can melee-parry all projectiles exactly once each, independent of if you destroy a projectile by shooting it (and receiving the same applied parry boost, but without flagging the projectile as 'parried').

The end result is what the community calls "Phantom boosts" or "phantom parries" - typically this is seen as shooting out an enemy projectile, then swapping to your katana and slashing at the point of impact in the air to get a second boost off the same projectile. You can see this at approximately 00:05.50 in this IL:

You can also see Dempsey restart the run right at the start because he missed the phantom parry - missing an occasional phantom parry is perfectly fine in Rushes generally, since they are a bit finnicky and precise, but in ILs it is absolutely the difference between WR and just like, top 10. For example, let's look at this level:

@reyathae got what was the perfect run on Godspeed after a good bit of grinding - in my time moderating the IL WR spreadsheet before I figured out the tech behind phantom boosts, no runs beat this run. Plenty of runs tied reya's record, but nothing could beat this time: it had the optimal line, it had the optimal godspeed timing, and it had 'lucky' frametimes (getting a frame at juuust the right time so your position is barely inside the goal, rather than having a 'faster' frame => you end up right in front of the goal. shit that isn't really controllable, but can still matter in a short and well optimized level like this). 5.935 was simply as low as Godspeed could be completed in, and that was that.

And then it turns out you can just fucking shoot Godspeed at the floor and parry the pinpoint spot that the bullet hit. You have 1.25s to parry that dead projectile you shot, but you have to hit a pinpoint spot in the floor as you're moving forward constantly and only have one chance to really do so. And it ends up making about a 0.2s difference in the end.

🦊 Phantom boosts are generally *the* last thing to optimize for in runs - you only really need to be doing this if you have literally everything else nailed, because they bring the execution ceiling for the game up to an *absurd* height. Phantom parries demand near-perfection in terms of both timing when you parry and where you click, since you have to keep track of where the phantom hitbox is lingering in the air.

🌾And as far as I can tell, it was done pretty intentionally by the devs - at least, that's the only reason I can figure out why there's the frozen 'hit decay' state where projectiles are intangible and have everything but their parry-ness made inoperable, rather than just simply despawning the projectile immediately after the collision.

Overall Takeaways

It's kinda hard to distill down all of the ways the different systems at play can interact, but in general, the rough optimization order in neon white is:

  • Keep the tightest lines for your route possible. Again, not getting into how levels are actually routed (though there's fun stuff involved here too! There's a few ILs that use my routes still, and maybe I'll do another post on the decision-making that lead to me figuring out those routes)
  • Maximize time on water, as waterboosting is the fastest sustained ground movement in the game
  • Card micro optimizations (maximizing fireball time on ground, godspeed usage timing to eke out all the exit bonus velocity falloff, shooting explosives at head height, etc)
  • First tier of bullet boosts and parries (shooting out projectiles that you have no hope of parrying, packing as many shots as possible onto purify bombs, etc)
  • Second tier of bullet boost execution (phantom parrying as many of them as possible)
  • Getting beneficial frametimes that lead to smoother position interpolation and hundredths of seconds on touching the goal (this is essentially just rng on the order of hundredths to thousandths of a second and only matters if you're grinding for like, really competitive short levels, thankfully)

Overall, the only real systems to think of in the game are:

  • the movement (and the implications of the two different velocity components)
  • the cards (and how to effectively eke out the most from your discard movement techniques)
  • the enemies (and how to efficiently use them for phantom boosts / kill them)
  • the levels (and how they're routed)

Misc Errata and Notes

Stomping deletes projectiles by invoking splash damage on everything nearby. You cannot phantom parry projectiles that get zooted by Stomp afaik.

There's a Thing In The Game where if a breakable object is broken, it checks to see if it had an enemy (or other breakable) 'anchored' to it, and then it kills that enemy/breakable if needed. This is mostly just used for things like "two jocks are standing on a glass platform and you shoot the platform". Except....

🔥The way the breakable anchoring actually works is that when any damageable is instanced, it looks for any other breakable that is directly underneath itself (raycast straight down, 1 unit long iirc) and hangs onto that object as the 'breakable platform' underneath it.

However, this can be any object that inherits the damageable interface - and that includes enemies! Unfortunately, there's only two places that matter: one bubble on Mirror at approx 9s is popped by using the zipline splash damage to break an explosive barrel inside the bubble, which breaks the glass platform and makes the bubble destroy itself on its next tick since the platform it was 'standing' on is no longer there. (notably, this isn't even actually that weird, aside from the fact that there's no override preventing bubbles from having this happen).

The other fun example of this is in The Third Temple at approximately 11s in. There's so much level-specific jank on the third temple. There's an invisible rotating circle at this point in the level, and when Green spawns the crystals, they spawn in fixed positions and then immediately snap to their spot on the wheel. And because the wheel is constantly spinning (and, get this, also rotates slightly for 1-2 ticks before the fucking timer starts or even the 'start level' leaderboard-splash screen is up, meaning that even if you have robotic execution precision, there's still some rng as to the actual rotation of the wheel!!!), it's effectively just the worst combination of a cycle and RNG in a game that is generally blessedly free of either of those.

We have some footage of us messing around with these crystals trying to figure out a pattern to which crystals anchored on each other - I modded in a 'do 1 damage to yourself if you have a 'platform' underneath yourself after initialization' thing just to have an indicator of which ones were affected so I could see if there was any reasonable way to actually use this in ILs - the unfortunate conclusion that Venny and I came to is that since there's no way to have it be predictable or usable (since there's no indication of which crystals can insta-break other crystals even when you do get a good cycle), it's effectively just a "sometimes crystals will Just Break and things will go a bit faster :)" bonus. (The original goal, once we noticed that crystals would randomly break when other crystals broke, was to find out if it would be possible to get a chain reaction of crystals that would break other crystals, since we had no clue why they would randomly break. "Surely there's an underlying reason and pattern and it's not Literally Just Random Bullshit in a speedrunning game", we thought. Unfortunately, this was jank that ended up having no practical use in this level, even in TASes. The general 'shit will break' mechanic is only useful for that one bubble on Mirror.

There's also another bit of jank in The Third Temple: if you deal enough damage to green in one of his vuln phases (e.g. when he's flying from point to point), he will become shielded again. This also turns on a bigger collider for him (since, yknow, invuln shield), and will make him destroy some of the rocket-crystals that you're supposed to grapple off of / need to destroy to make him move again!

This only applies to like two of the crystals in the third temple - one of them is one you can break early on and speed things up a bit, and the other one is a "you need to not burst him and put his shield up too early, or he breaks that crystal and you have nothing to grapple to" kind of deal - overall, just a small quirk to keep in mind.

Other minor details I just wanted to show off

This is easiest to see on the Miracle katana discard screen (which is only available in Hell Rushes (single life level rush modes; the Heaven Rushes are the same but with unlimited lives and retries)), but the cards all show both the weapon and the movement ability for discarding them!

a picture of the cards side-by-side. I'm getting to the details I promise!!

So, in order, we have:

  • Elevate. There's the pistol silhouette, and also the little double-arches going over it, for the double jump it gives you!
  • Godspeed. There's the marksman's rifle that you can tap-fire, and the arch around its tip, for the dash forwards that you do.
  • Fireball. There's the shotgun, and the arch showing the recoil pushing back from it! The shockwave also doubles as the front of a fireball, sorta, though it doesn't actually knock you back when you use it or anything.
  • Stomp. There's the uzi, and the downwards arcs. You know, for the stomp.
  • Purify! We have our beautiful assault rifle, and the little star-orb as the bomb you can shoot... and there's the explosion/pressure wave encircling it!
  • Dominion - rocket launcher and grappling hook. The most straightforward; they are both Just There.
  • The Book of Life.... honestly I think they did good on this one; a telefrag card should look fancy as hell.
🦉 I have so many tattoo ideas based on these cards. Seriously, just look at all the details on their designs here and here.

Thanks to....

The general Neon White community! I am not omniscient and have a ceiling on how well I can play; it's hard to just notice a lot of this tech intrinsically - if it wasn't for runners asking me to figure out why a weird thing happened to them in a run, I wouldn't have figured out even half of this stuff since I didn't even know what to look for!

@pupwitch for pointing me to dnSpy and melonLoader, both of which were wildly useful in my reverse engineering and modding of the game 🧡

my various friends who kept beating my times and making me demolish their times >:D (and @rythmyr for being the only person who could consistently take WRs back from me!! how dare...)

@ciswoman for chosting about neon white, which reminded me that Hey I Can Simply Post This Shit

Imhex, which has become my favorite hex editor for anything that's just structured binary data. It's a bit tedious to use if you just want to patch a couple addresses, but it is phenomenal for producing annotated views of binary data, and is great for just introspecting deep into stuff. Seriously:

a screenshot of a very colorful hex editor. honestly not sure how to meaningfully describe it.

I know that this looks like soup to most people, but that's kinda the point - these are my annotations and visualizations built up; I can turn off a lot of the individual highlighting of struct fields or whatever (and do when I'm digging into things With Intent rather than just popping it open), but there's just.... a lot you can do with imhex if you're digging into some structured binary data!

🌕 Thanks for giving this a read and bearing with! We tend to look at a lot of stuff in computing as Systems and Interfaces interacting, with layers and layers of abstraction that you can peel back and dig into - with adjacent systems both appearing as monoliths (say, the boundary between userspace networking and The Kernel) as well as having boundary interfaces (syscalls and returns) and layers of abstraction you can dig into in either direction.

The paradigm we use for a lot of that still applies really well for reverse engineering games and the like - we have the different classes involved in handling player inputs as the boundary between the player and the game, and there's just.... lots of systems at play in the game, from enemy AI ticking, to the engine's frame-vs-tick updates, to the general player movement class, to the player cards class, to projectiles, etc. There's lots of layers to peel back across all of this, and while understanding a lot of those layers and systems helps us think about and remember a lot of these details, the implementation details don't always matter for speedrunning purposes, or are just more immediately confusing than knowing the practical details of how to do a thing. For example, there's the baseDamageable interface that both Enemies and things like card chests, breakable walls and platforms, etc all inherit from, and it's useful to understand how that can matter in logic that specifically just looks for any damageable and not just enemies - but practically, the detail doesn't matter for running, and so it doesn't get brought up generally!


You must log in to comment.

in reply to @hecate's post:

This is so sickkkkkkk thank you for writing it!!!!!!

When I was playing I noticed a somewhat consistent piece of micro tech but never timed it by frames I think, but I'm curious if it's ever been talked about or used - when you Stomp onto the edge of a goal w/ enemies around the goal, the levels always felt like they completed before I hit the ground, like Stomp was procing its attack on the goal somehow, vs when I stomped directly into the goal. Easiest to see in Prepare and i think Guardian and Mountain have ends that do it as well? Just always been curious about it.

oh yeah, so - you can actually stand on the like, top edges of the goal? like just the outer frame of it, but not the actual general top of it. afaik thats just stomp landing on the top frame, doing its splash damage and killing everything, and then the goal activates while you're colliding with it

it's definitely used in a lot of routes :) it's essentially just the ideal line to take, since you usually want to stop stomp as soon as you can

it is :) i just kinda mentally lump it in under 'routing' lol

there's just sooo much nuance to how to do things Optimally in neon white and i just love how much complexity it adds, generally