QA: "the sfx is too loud and scratchy." Drop-in asset swap — no engine change.
Replace the 9 synthesized cues (swap, match, combo1..combo5, win, lose) with
real free-licensed (CC0 1.0) sound effects from Kenney's Interface Sounds /
Digital Audio packs:
swap <- pluck_002 match <- confirmation_002
win <- powerUp7 lose <- minimize_006
combo1..5 <- glass_001 pitch-laddered up a pentatonic run (0,+2,+4,+7,+9 st)
via resample DSP -> ascending fundamentals 1918..3226 Hz.
Every cue: mono / 44100 Hz / Int16 PCM, <=0.54 s. Whole bank peak-normalized to
-9 dBFS, so peak AND RMS are well below the old synth (old peak -0.7..-2.9 dB,
mean -5.8..-9.3 dB; new peak -9.0 dB, mean -22..-30 dB). Audibly cleaner — no
synth harmonics.
LICENSE.txt rewritten with real per-file provenance (source URL + CC0). clear.wav
(already a CC0 Kenney clip, not loaded by the engine) left unchanged. Engine
(audio.sx/board_view.sx/main.sx) untouched; sim build loads all 9 cues, zero
"load failed".
Regenerate the board art in a Candy-Crush palette via real image
generation (codex imagegen tool), keeping each PNG's exact dims and
source format so no code/layout changes are needed:
- background.png: 863x1822 opaque RGB — bright bubblegum-pink ->
lavender -> sky-blue candy gradient with soft bokeh sparkles
(was the dark purple/indigo/teal gradient).
- cell.png: 128x128 RGBA — light glossy frosted candy tile with a
warm pink/lavender tint, keeping it light so the 6 gems stay legible.
Generated at imagegen-native sizes, then center-cropped (background, to
preserve aspect) and resized with sips to the exact target dims/format.
Refresh the resting-board goldens (p9_polish, p4_board) captured at
M3TE_ANIM_TIME=0 to show the new palette with all 6 gems legible.
Produce the first deliverable of the Candy-Crush vibe pass: a bank of bright,
glossy, higher-pitched SFX WAVs under assets/audio/, all in the exact canonical
iOS System-Sound format clear.wav uses (mono, 44100 Hz, signed-16-bit PCM).
Bank: swap, match, combo1..combo5 (ascending pentatonic run C6 D6 E6 G6 A6),
win (ascending arpeggio), lose (descending stinger). Every cue sits above
clear.wav's ~784 Hz fundamental; combo1<..<combo5 step strictly upward
(1047<1175<1319<1568<1760 Hz). Each loads via AudioServicesCreateSystemSoundID
with status 0.
Synthesized by the build-time tools/synth_audio.py (pure-Python additive
synthesis; the app never runs it) and converted with afconvert. Pitch verified
with tools/measure_pitch.py. Provenance (CC0) recorded in LICENSE.txt. No sx
code changes — engine wiring is P10.2/P10.3.
Feasibility spike outcome: iOS audio from sx is feasible with no sx-library
change. System Sound Services is plain C, reached with the same `#foreign`
FFI uikit.sx already uses (UIApplicationMain / dlsym / CACurrentMediaTime);
AudioToolbox + CoreFoundation are linked per-target in build.sx.
Smallest viable SFX: one short CC0 clip (Kenney Interface Sounds, CC0 1.0)
played when a swap clears a match. Purely additive — audio.sx reads/writes
no score/board/move state; the wiring in board_view only adds a call.
- audio.sx: load clear.wav once, AudioServicesPlaySystemSound on clear
- board_view.sx: trigger sfx_clear() on a legal swap that clears (>=1 round)
- main.sx: allocate + init g_audio at boot
- build.sx: link AudioToolbox + CoreFoundation on iOS
- assets/audio/clear.wav (+ one-line CC0 credit in LICENSE.txt)
Verified: ios-sim build links; 18/18 tests pass; sim boot log shows
"[sx] audio: clear cue loaded" (AudioServicesCreateSystemSoundID succeeded,
asset shipped in the bundle and decoded).
Add a purely-visual, transient juice layer over a committed move — score
popups + a tinted particle/flash burst at the clears — with no change to the
model, score, moves, or settled board.
- assets/fx/particle.png: key the painted transparency checkerboard out of the
provided particle art to real alpha (8-connected border flood fill +
smooth luminance falloff that preserves the soft glow), downscaled to a
256x256 RGBA white sparkle. tools/key_particle.sx is the reproducible tool.
- board_fx.sx: BoardFx (live particle bursts + one "+points" popup) and
BoardFxAssets. The engine image path samples texture*white (no draw-time
tint), so the white sprite is tinted per gem colour at LOAD time into one
texture per colour; a burst animates by scale (grow -> shrink) and the soft
texture edges carry the fade. Combos (cascade depth > 1) burst bigger and the
popup is larger + gold. All driven by delta_time and self-pruning.
- board_anim.sx: AnimMove carries the model's cascade.awarded so the popup
shows the real payout without re-deriving any scoring in the view.
- board_view.sx / main.sx: wire BoardFx + the tinted assets, tick each frame,
spawn on a legal commit, and render bursts (clipped to the grid) under the
popups (drawn on top). Input-lock (BoardAnim.active) is untouched; FX never
gate input and may outlast the move slightly before vanishing.
Goldens (iPhone-17-class sim, iOS 26): p6_fx.png (combo: gold "+480" + bursts
mid-cascade), p6_fx_match.png (single match: "+30" + red burst), p6_fx_after.png
(settled board, FX fully gone). Gate: ios-sim build links, 15/15 logic tests
green (scoring/cascade goldens unchanged).
- assets/board/background.png: full-screen 863x1822 RGB gradient (opaque,
exact 9:19), re-encoded from the provided source.
- assets/board/cell.png: 128x128 RGBA rounded glass tile. The painted
gray/white transparency checkerboard is keyed to real alpha via an
8-connected flood fill from the border, using a low-saturation predicate
(checker spread<=12, panel is bluish spread>=20) so the rounded panel is
preserved; the clean RGBA is then downscaled 1254->128 with LANCZOS.
- assets/fonts/default.ttf: Lato Regular, SIL Open Font License 1.1
(tyPoland Lukasz Dziedzic, Reserved Font Name "Lato"); OFL.txt shipped
alongside per the license.
All three bundle into the ios-sim M3te.app build and decode cleanly.
Key the painted checkerboard out of the provided gem art via an
edge-connected flood fill (8-conn, light+desaturated predicate) so the
gems' interior specular highlights are preserved, then slice the 6 gems
by alpha bbox, premultiply-resize each to fit 108x108, and center one
per 128x128 transparent cell in index order (red, orange, yellow,
green, blue, purple).
Stand up build.sx (macos + ios/ios-sim targets, bundle id
co.swipelab.m3te, output sx-out/ios/M3te.app, assets dir) and a minimal
main.sx that brings up the platform (UIKit+Metal on iOS, SDL3+GL on
macOS) and renders a solid-clear frame. Add assets/ and goldens/
directories and a .gitignore for build artifacts.
Modeled on game/build.sx and game/main.sx; modules resolve from the
compiler binary with no -L flag.