Deep Sea Bomber
A calm bedtime arcade game set in a glowing ocean trench






01 The story
A low-stress Flutter game for iOS and Android: steer a small submarine with single taps and drop slow-sinking depth charges on rigs and crab harvesters below. I made it as a late-night wind-down toy, with a dark bedtime palette, no harsh flashes, a 12-stage campaign and 27 hulls.
Deep Sea Bomber started with a question: what would a classic arcade bomber, in the spirit of Atomic Bomber, feel like if it were designed to help you wind down instead of wind up? The answer is a slow, forgiving underwater game. The sub glides on its own, you tap to change its heading, and depth charges sink gently onto targets on the seabed.
The design rules are strict. Colors stay in deep abyssal blues with soft cyan and seafoam accents, pure-white pixels are kept to a tiny share of the screen, and nothing flashes or explodes. Dying dissolves the sub into a ring of dark bubbles and dims the screen. You get four lives, a grace period on entry, dodgeable torpedoes, and hazards that arrive late and sparse. Around that sit a 12-stage campaign through six biomes, a final 'Vent Guardian' you soothe rather than kill, an endless mode and a hangar of 27 hulls.
There's no game engine. Everything is drawn with Flutter's CustomPainter. Every creature and prop is a sprite paired with a depth map, and a GLSL fragment shader turns each pair into live 2.5D: relief lighting plus parallax that makes crabs and rigs seem to turn toward the passing sub. Lit frames are cached per quantized light and parallax angle, so only nearby sprites re-bake each frame. Sound effects are synthesized as raw PCM at runtime, so no SFX files ship.
The art is generated: the sprites and depth maps with Gemini's image model, and the three music tracks with Google's Lyria. The game is feature-complete with a passing test suite and CI. I built version 1.0.1 as a signed Android App Bundle for a first Google Play upload in July 2026, along with designed store screenshots and a feature graphic. There's no public store listing yet.
02 What it does
- 01
Tap-to-vector steering: one tap sets a new glide heading, or hold and drag to steer continuously
- 02
Slow-sinking depth charges that inherit the sub's speed, with a seabed reticle showing where a drop would land
- 03
12-stage campaign on a descending world map across six biomes (shelf, kelp, wreck, rigs, mooring, black smoker), plus an endless mode
- 04
A final-stage 'Vent Guardian' leviathan that you calm with six hits instead of destroying
- 05
Hangar of 27 hulls with small handling differences (glide speed, reload, spare charges), and 15 paint liveries
- 06
Rigs that fire bubble torpedoes, walking crab harvesters, drift mines, anglerfish, thermal geysers, currents and relics to carry up
- 07
Bedtime presentation: dark palette, no bright flashes, soft death animation, gentle haptics, separate sound and music controls
03 How it's built
A fully offline, single-device game with no backend. Art (Gemini) and music (Lyria) are generated ahead of time, processed by Python tools and bundled as assets. At runtime the engine simulates, a CustomPainter draws, and a fragment shader lights each sprite from its depth map.
All 6 connections, as text
- Flutter app (menu, campaign, hangar) → Game engine (simulation): starts runs, hull stats
- Game engine (simulation) → CustomPainter renderer: world state each frame
- CustomPainter renderer → relief.frag depth shader: sprite + depth map
- Game engine (simulation) → Runtime PCM synth + music: SFX events
- Flutter app (menu, campaign, hangar) → Local settings / progress: save progress
- Asset tools: sprites, depth maps, music → CustomPainter renderer: bundled PNG assets
04 Engineering notes
2.5D pipeline: each sprite has a matching depth map; a GLSL fragment shader (relief.frag) contrast-stretches the depth, derives surface normals for lighting, and aims parallax at the sub so props seem to turn toward it
Lit sprite frames are baked offscreen and cached per quantized light and parallax angle, so only nearby sprites re-bake each frame
No game framework: the renderer and simulation are hand-written Dart (CustomPainter), with a depth-sorted parallax camera and toroidal left-right screen wrap
Sound effects (sub drone, release thump, hit ping, boom) are synthesized as raw PCM at runtime and written to temp WAVs for gapless looping. No SFX files ship
Performance pass: per-sprite saveLayer calls replaced by opacity in the paint color, a full-screen blur's bounds tightened, and a frame-time benchmark test against a 16.67 ms budget
Flutter tests (campaign, hulls, ammo, audio synthesis, sprite loading) run in GitHub Actions on every push; test scripts also render preview frames and build the app icon from the game's art
05 By the numbers
- 12
- Campaign stages
- 27
- Submarine hulls
- 15
- Paint liveries
- 110 PNGs
- Generated art files (sprites + depth maps)
- 93 MB
- Signed Android App Bundle, v1.0.1
06 Stack
App
- Flutter
- Dart
- CustomPainter
- GLSL fragment shader (FragmentProgram)
Data
- shared_preferences (settings, best run, unlocks)
Infrastructure
- GitHub Actions CI (flutter analyze + flutter test)
Tooling
- audioplayers
- path_provider
- flutter_launcher_icons
- Python asset tools (Pillow, NumPy)
- Gemini image model for sprites and depth maps
- Google Lyria for music
Lines of code (approx.)
- Dart14,213
- Python1,223
- GLSL143
≈ 15,579 lines
Repositories
- deep_sea_bomberprivate
Git history starts 2026-06-30, when I turned the existing project into a repo. The Flutter project files on the SSD copy are dated 2026-06-22. All 36 commits fall between June 30 and July 6, 2026.