Game 1 Unit 13 of 16

Difficulty Progression

Keep the challenge fresh. Dynamic speed adjustments reward skilled players with faster, more intense gameplay.

81% of SID Symphony

The Boredom Problem

A fixed difficulty creates two failure modes. Beginners struggle and give up. Experts get bored and move on. Neither plays for long.

The solution: dynamic difficulty. As players demonstrate skill, increase the challenge. As they struggle, ease off. The game stays in the “flow” zone — difficult enough to engage, not so hard it frustrates.

Speed as Difficulty

In a rhythm game, note speed is the obvious difficulty lever. Faster notes demand quicker reactions. We already have a skill indicator: the multiplier. High multiplier means the player is hitting notes consistently. Reward that consistency with a greater challenge.

; Difficulty progression constants
; Lower number = faster notes (fewer frames between moves)
SPEED_1X          = 4           ; Normal speed at 1x multiplier
SPEED_2X          = 3           ; Faster at 2x
SPEED_3X          = 3           ; Same at 3x (challenge plateau)
SPEED_4X          = 2           ; Fastest at 4x multiplier

At 1x, notes move every 4 frames. At 4x, they move every 2 frames — twice as fast. The jump from 2x to 3x is a plateau, giving players a brief respite before the final push.

Table Lookup

Rather than calculate speed from multiplier each time, we use a lookup table. This is a common 6502 pattern — trading a few bytes of memory for faster execution:

; Speed lookup table (indexed by multiplier-1)
speed_table:
            !byte SPEED_1X          ; 1x multiplier
            !byte SPEED_2X          ; 2x multiplier
            !byte SPEED_3X          ; 3x multiplier
            !byte SPEED_4X          ; 4x multiplier

The multiplier ranges from 1-4, but array indices start at 0. We subtract 1 before indexing:

update_speed:
            ldx multiplier
            dex                     ; Convert 1-4 to 0-3 index
            lda speed_table,x
            sta current_speed
            rts

Three instructions to look up and store the new speed. Fast and simple.

Triggering Speed Changes

Speed updates whenever the multiplier changes. That happens in two places: when it increases (combo threshold reached) and when it resets (miss).

            inc multiplier
            ; Trigger border flash on multiplier increase
            lda #BORDER_FLASH_TIME
            sta border_flash_timer
            ; Update speed for new multiplier
            jsr update_speed
            ; Reset combo and multiplier on miss
            lda #$00
            sta combo_count
            lda #$01
            sta multiplier
            ; Reset speed to normal
            jsr update_speed

Using Current Speed

The game loop uses current_speed instead of the hardcoded NOTE_SPEED:

            ; Handle note movement
            dec move_timer
            bne ug_no_move
            lda current_speed       ; Use current speed based on multiplier
            sta move_timer
            jsr move_notes

When move_timer hits zero, we reload it from current_speed. At higher multipliers, this value is smaller, so the timer expires more often, moving notes more frequently.

Initialisation

Reset the speed variable when starting a new game:

            ; Reset speed
            lda #SPEED_1X
            sta current_speed

The Feel

Watch how this changes the game:

  1. Player starts at 1x — comfortable pace
  2. Builds combo, hits 2x — notes speed up
  3. Adapts to new speed, hits 3x — same speed (breathing room)
  4. Pushes through to 4x — notes fly
  5. One miss — back to 1x, sudden slowdown

The speed changes create drama. Reaching 4x feels like an achievement. Losing it feels like a fall. The game becomes a story of rises and crashes, not just a steady rhythm.

Why Not Continuous Scaling?

We could calculate speed as 5 - multiplier. But discrete steps feel better. Each multiplier level has a distinct character. Players can think “I’m at 3x speed” rather than feeling lost on a sliding scale.

The plateau at 3x is deliberate. After two speed increases, players need time to consolidate. The jump to 4x then feels special — the final challenge.

Alternative Approaches

Other ways to increase difficulty:

  • More notes: Spawn notes more frequently at higher multipliers
  • Narrower windows: Shrink the Perfect zone as skill increases
  • Pattern complexity: Introduce chord hits earlier at higher levels
  • Visual noise: Add distractions as difficulty climbs

Speed is the simplest lever. The others add complexity without proportional payoff.

What’s Next

The game now adapts to player skill. In Unit 14, we add high score persistence — saving the best score so players have a target to beat across sessions.