Updated 2026-05-20 with full mechanics, src/ layout, and challenge knobs.
TL;DR
A brick-breaker whose wall is a GitHub contribution graph. The ball + paddle live below; bricks are the commit cells, coloured by activity level and harder to break the brighter green they are. Clearing the wall = clearing a year of commits.
Ships in apps_market/ (opt-in via the on-device App Market) so it doesn't burn a default-drawer slot.
🎮 Core loop
- App fetches the player's contribution graph (reuses
apps/commits/'s cached fetch + offline fallback).
- Graph renders as a brick wall at the top of the screen — 7 rows × ~32 most-recent weeks.
- Paddle at the bottom (D-pad LEFT/RIGHT). A launches ball.
- Ball bounces off paddle / walls / bricks. Bricks lose HP per hit, shift shade, vanish.
- Drop the ball below the paddle → lose a life. 3 lives total.
- Clear the whole wall → "year cleared" screen with time + score.
🧱 Brick model
| GitHub shade |
HP |
Base points |
Visual |
| Level 0 (no contribs) |
— |
— |
empty cell |
| Level 1 (light green) |
1 |
10 |
#9be9a8 |
| Level 2 (mid green) |
2 |
25 |
#40c463 |
| Level 3 (deep green) |
3 |
50 |
#30a14e |
| Level 4 (darkest) |
4 |
100 |
#216e39 |
Each hit drops HP by 1 and shifts the brick one shade lighter, so the player sees damage build up visually before the brick pops.
🔥 Difficulty / challenge knobs
The trick with breakout is that "just bricks" gets boring fast. These are the layers that make it sticky:
- Paddle-position deflection. Ball bounce angle is set by where on the paddle it hits, classic Arkanoid feel. Centre = straight up, edges = ±60°. No 1980s "ball always returns at the same angle" boredom.
- Speed ramp. Ball base speed increases by 5% every 25 bricks broken. Caps at 2× start.
- Combo multiplier. Bricks broken between paddle touches stack a multiplier (×2 after 5, ×3 after 12, ×4 after 25). Drops back to ×1 the moment the paddle touches the ball.
- Power-up drops. Every Nth brick (~1/30) drops a falling power-up the paddle can catch:
- WIDE — paddle 1.5× width, 10 s.
- MULTI — ball splits into 2; lose all to lose a life.
- SLOW — ball 60% speed, 8 s.
- STICKY — next paddle-bounce holds the ball; press A to release with aim.
- Streak columns. If the source data has 5+ consecutive level-4 cells in a column (a "real" contribution streak), the column reads as a single boss-column: each cell in it has +1 HP but clearing the whole column gives a flat +500 bonus.
- Dense weeks = harder. A graph from a busy contributor is genuinely a tougher level than a sparse one. Use someone else's username in
secrets.GITHUB_USER for a different challenge.
- Year mode vs Endless mode. Year = one year, finite bricks, finish line is "clear the wall." Endless = infinite scrolling weeks from oldest data + procedural fallback when data runs out; ball gets faster every cleared row.
🎨 Layout (320 × 240, landscape)
┌───────────────────────────────────────────────────┐ ← y=0
│ COMMIT BREAKER 12,540 ×3 ❤❤❤ │ header (28 px)
├───────────────────────────────────────────────────┤
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ ┐
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ │ brick wall
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ │ 7 × ~32
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ │ ~64 px tall
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ │
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ │
│ ░░▓▓██▓▓░░░▓▓██░░░░▓▓██▓▓░░░░██▓▓░░▓▓░░ │ ┘
│ │
│ ⚙ MULTI │ power-up drop
│ │
│ ○ │ ball
│ │
│ │
│ ▬▬▬▬▬▬▬▬ │ paddle (D-pad)
├───────────────────────────────────────────────────┤
│ A=launch L/R=move B=pause HOME=back │ hint (16 px)
└───────────────────────────────────────────────────┘ ← y=239
Cell pitch ~9×8 px keeps the graph visually GitHub-like at this scale.
🎮 Controls
| Button |
Action |
| LEFT / RIGHT |
Move paddle |
| A |
Launch ball / catch with STICKY power-up |
| B |
Pause |
| HOME |
Exit (intercepted by OS — on_home_press not needed) |
| (optional) IMU tilt |
Alternate paddle control; toggle in pause menu |
📁 Directory (new src/ layout)
apps_market/commit_breaker/
├── __init__.py
├── manifest.json name, version, author, icon
├── main.py 3-line shim → re-exports App
└── src/
├── __init__.py
├── app.py lifecycle hooks only
├── game.py pure logic — ball physics, brick HP, collisions
├── render.py drawing (wall, ball, paddle, HUD, overlays)
├── grid.py contribution-data → brick grid (reuses commits/ cache)
└── powerups.py drop / catch / effect timer logic
See https://oreo.elixpo.com/docs/apps/ for the full app-writing convention and apps/snake/ for the reference layout.
🧩 Data source
Reuse apps/commits/'s fetch:
from apps.commits.src.commits import fetch_contributions, demo_grid
grid = fetch_contributions(secrets.GITHUB_USER) or demo_grid()
# grid is a list of 7 rows × N weeks of ints 0..4
Cache TTL is already 1 h via oreoOS.cache so the brick wall doesn't refetch every launch. Offline / first-boot → falls back to a hand-crafted demo grid.
✅ Acceptance criteria
🎯 Stretch goals
- IMU-tilt paddle (toggle in pause menu).
- Power-ups (any subset of WIDE / MULTI / SLOW / STICKY).
- Endless mode.
- Streak-column boss bonus.
- Particle effect on brick pop (reuse pixelfont scale for a quick "+25" flash).
- Sound — drop a tiny PWM buzzer beep on paddle-bounce / brick-pop if the badge ever gets one.
💬 Open questions
- Paddle physics curve. Linear angle-by-position is the safe default; a slight non-linear curve (more deflection near the edges) tends to feel better. Tune in playtest.
- IMU as primary or alternate? D-pad is more reliable; IMU is fun but error-prone at high speed. Default to D-pad, IMU as opt-in.
- Should "today's cell" be a boss? Could make the bottom-right cell (most recent day) a 5-HP brick worth +200 points, as a small narrative beat.
TL;DR
A brick-breaker whose wall is a GitHub contribution graph. The ball + paddle live below; bricks are the commit cells, coloured by activity level and harder to break the brighter green they are. Clearing the wall = clearing a year of commits.
Ships in
apps_market/(opt-in via the on-device App Market) so it doesn't burn a default-drawer slot.🎮 Core loop
apps/commits/'s cached fetch + offline fallback).🧱 Brick model
#9be9a8#40c463#30a14e#216e39Each hit drops HP by 1 and shifts the brick one shade lighter, so the player sees damage build up visually before the brick pops.
🔥 Difficulty / challenge knobs
The trick with breakout is that "just bricks" gets boring fast. These are the layers that make it sticky:
secrets.GITHUB_USERfor a different challenge.🎨 Layout (320 × 240, landscape)
Cell pitch ~9×8 px keeps the graph visually GitHub-like at this scale.
🎮 Controls
on_home_pressnot needed)📁 Directory (new src/ layout)
See https://oreo.elixpo.com/docs/apps/ for the full app-writing convention and
apps/snake/for the reference layout.🧩 Data source
Reuse
apps/commits/'s fetch:Cache TTL is already 1 h via
oreoOS.cacheso the brick wall doesn't refetch every launch. Offline / first-boot → falls back to a hand-crafted demo grid.✅ Acceptance criteria
apps_market/commit_breaker/with the src/ layout above.commit_breaker_icon.png(prompt atprompts/icons/commit_breaker_icon.md— generate with Pollinations).apps_market/commit_breaker/hiscore.txt.🎯 Stretch goals
💬 Open questions