Arrow key causes delta jump in spritekit.

Hello,

I have a problem where my macOS (Sequoia) Spritekit game spikes delta during the first press of an arrow key. I've test this with a small program, code below:

//
//  GameScene.swift


import SpriteKit
import GameplayKit

class GameScene: SKScene {
    var lastTime: TimeInterval = 0
    
    override func keyDown(with event: NSEvent) {
            print("---------------> delta keyDown: \(event.characters!) keyCode: \(event.keyCode)")
    }
    
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
        print("update begins")

        let dt: CGFloat
        if lastTime > 0 {
            dt = (CGFloat(currentTime - lastTime))
        } else {
            dt = 1.0 / 60.0
        }
        if dt > (1/30) {
            print("************************************ delta spike ", dt)
        }
        lastTime = currentTime
        print("dt: ", dt)
        print("update ends")

    }
}

Example output:


update begins
************************************ delta spike  0.03381687504588626
dt:  0.03381687504588626
update ends
update begins
dt:  0.016670208307914436
update ends

As you can see, when I press left arrow key in this case I get a big delta spike. There's no spike with further presses of the arrow key.

Other keys, such as the common W A S D controls for games, do not cause this delta spike.

Answered by DTS Engineer in 885794022

The delta spike on the first arrow key press is a macOS behavior. Arrow keys take a different path through the responder chain than regular character keys — they route through interpretKeyEvents(_:), which likely triggers lazy initialization of the text input system on first use. That one-time setup cost is what causes the spike. Regular keys like WASD don't take this path, which is why they're unaffected.

Your addLocalMonitorForEvents workaround bypasses the responder chain, which avoids the initialization cost. However, returning nil from the monitor consumes all key-down events, which will break keyboard shortcuts (Cmd+Q, Cmd+W, etc.) and prevent events from reaching other responders.

The standard solution in game development is to clamp your delta time to a maximum value:

let dt: CGFloat
if lastTime > 0 {
    dt = min(CGFloat(currentTime - lastTime), 1.0 / 30.0)
} else {
    dt = 1.0 / 60.0
}
lastTime = currentTime

This protects against all sources of frame time spikes — not just arrow keys, but also system interruptions, thermal throttling, window resizing, and background transitions. Any game that uses delta time to scale movement or physics should have a clamp like this.

This seems to have resolved the problem. Added to ViewController.swift in viewDidLoad:

    NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in self.skView.scene?.keyDown(with: event)
        return nil }

The delta spike on the first arrow key press is a macOS behavior. Arrow keys take a different path through the responder chain than regular character keys — they route through interpretKeyEvents(_:), which likely triggers lazy initialization of the text input system on first use. That one-time setup cost is what causes the spike. Regular keys like WASD don't take this path, which is why they're unaffected.

Your addLocalMonitorForEvents workaround bypasses the responder chain, which avoids the initialization cost. However, returning nil from the monitor consumes all key-down events, which will break keyboard shortcuts (Cmd+Q, Cmd+W, etc.) and prevent events from reaching other responders.

The standard solution in game development is to clamp your delta time to a maximum value:

let dt: CGFloat
if lastTime > 0 {
    dt = min(CGFloat(currentTime - lastTime), 1.0 / 30.0)
} else {
    dt = 1.0 / 60.0
}
lastTime = currentTime

This protects against all sources of frame time spikes — not just arrow keys, but also system interruptions, thermal throttling, window resizing, and background transitions. Any game that uses delta time to scale movement or physics should have a clamp like this.

Arrow key causes delta jump in spritekit.
 
 
Q