Skip to content

Breakout Task paddle can move past the canvas walls #1640

Open

Description

@joshunrau

The Breakout Task paddle can move past the canvas walls. The keyboard clamp checks the paddle position before moving it 7px, so the last step goes over the edge. In the 480px canvas (scale 1) the paddle stops at x = 405.5 against a limit of 405 on the right, and at x = -0.5 against 0 on the left. The overshoot depends on the scale: at scale 1.25 it is about 6px. The mouse handler has no clamp at all. It centers the paddle on any pointer inside the canvas, so the paddle can hang up to half its width (37.5px at scale 1) off either side. The patient sees part of the paddle cut off at the edge of the game.

Where

packages/instrument-library/src/interactive/DNP_BREAKOUT_TASK/index.ts:203-207 (keyboard):

if (rightPressed && paddleX < width - paddleWidth) {
  paddleX += 7;
} else if (leftPressed && paddleX > 0) {
  paddleX -= 7;
}

packages/instrument-library/src/interactive/DNP_BREAKOUT_TASK/index.ts:94-99 (mouse): paddleX = relativeX - paddleWidth / 2 for any 0 < relativeX < width.

Reproduce

  1. Open the Breakout Task in a viewport that gives the 480x320 canvas, and press Start.
  2. Hold the right arrow until the paddle stops.

Actual: the paddle comes to rest at x = 405.5, so its right edge is at 480.5, past the 480px canvas. Holding the left arrow leaves it at x = -0.5.
Expected: the paddle stops flush with the wall, at x = 405 on the right and x = 0 on the left.

You can also move the mouse to the far right edge of the canvas. Actual: half the paddle is drawn off the canvas. Expected: the paddle stops at the wall.

Tests

packages/instrument-library/src/__tests__/DNP_BREAKOUT_TASK.test.ts. The tests should stop the paddle at the right wall and should stop the paddle at the left wall were written loosely on purpose: they only assert that the paddle stops within one 7px step of the wall, so they pass with or without the fix. When fixing, tighten them to expect(stoppedX).toBe(CANVAS_WIDTH - PADDLE_WIDTH) and expect(stoppedX).toBe(0). If the mouse path is clamped too, add a case that moves the mouse near each edge.

Suggested fix

Clamp after moving, e.g. paddleX = Math.min(paddleX + 7, width - paddleWidth) and paddleX = Math.max(paddleX - 7, 0). Apply the same [0, width - paddleWidth] clamp in mouseMoveHandler.

Found while raising unit test coverage in #1632.

Activity

  1. added
    BugType: existing behavior is wrong
    Priority: LowNice to have, cosmetic or speculative
    Difficulty: LowIsolated change, about 2 hours or less
    Area: InstrumentsInstrument authoring API, bundling, runtime, rendering, measures and the built-in library
    Good First IssueWell-scoped, low-risk and self-contained; a good entry point for a new contributor
    on Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: InstrumentsInstrument authoring API, bundling, runtime, rendering, measures and the built-in libraryBugType: existing behavior is wrongDifficulty: LowIsolated change, about 2 hours or lessGood First IssueWell-scoped, low-risk and self-contained; a good entry point for a new contributorPriority: LowNice to have, cosmetic or speculative

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions