Replace the pitch-laddered single-pop combo ladder with FIVE distinct REAL
Match FX cues from the user's Triple Treat SFX pack (Match SFX/), ordered to
convey cascade escalation by spectral brightness / high-frequency energy
(combo1 dullest -> combo5 sparkliest):
combo1 <- Match SFX/Match FX 2-RCM.wav (dark/full, centroid ~1.7 kHz)
combo2 <- Match SFX/Match FX 4-RCM.wav (warm mid, centroid ~2.1 kHz)
combo3 <- Match SFX/Match FX 6-RCM.wav (bright mid, centroid ~3.2 kHz)
combo4 <- Match SFX/Match FX 7-RCM.wav (rich+bright, centroid ~4.7 kHz)
combo5 <- Match SFX/Match FX 3-RCM.wav (sparkly, centroid ~6.8 kHz)
The Match FX set does not cleanly pitch-ascend, so brightness is the ordering
signal; spectral centroid ascends monotonically 1.68 < 2.09 < 3.18 < 4.70 <
6.77 kHz. Each down-mixed to mono, trimmed to a 0.50 s onset window, eased in
(~6 ms) and rounded out with a 150 ms cosine fade-out, peak-normalized -15 dBFS.
Pure drop-in: audio.sx and all wiring are untouched; swap/match/win/lose are
byte-identical to P10.8. The depth->cue-index mapping (cascade_cue, fx_combo)
locks the integer mapping, not the audio content, so both tests stay valid.
LICENSE.txt combo provenance updated to the real Match FX sources.
Replace the 9-cue bank with best-fit selections from the user-provided
Triple_Treat_SFX.zip, converted to the engine format (mono / 44100 / Int16,
<= ~600 ms) and peak-normalized to a gentle, consistent -15 dBFS. Drop-in:
audio.sx and all wiring are untouched; only assets/audio/** changes.
Per-cue source within the pack:
swap <- Transition SFX/Swipe FX 1 (light swipe = the swap gesture)
match <- Pop:Bubble SFX/Pop FX 5 (juicy candy pop, first clear)
combo1..5 <- Pop:Bubble SFX/Pop FX 3 (one pop pitch-laddered +0/+2/+4/+7/+9
semitones; the pack's Match set does not ascend monotonically)
win <- Success:Power-Up SFX/Power Up FX 1 (short triumphant)
lose <- Fail SFX/Fail FX 2 (gentle tonal, not boomy)
combo1..5 ascend in fundamental: 687 < 771 < 865 < 1029 < 1155 Hz. The 30 MB
pack and its .meta/__MACOSX cruft are not committed; LICENSE.txt records the
exact per-cue source file within the pack.
DSP-soften the existing P10.6 CC0 bank in place — same filenames, same
canonical WAVE/mono/44100/Int16 format, drop-in (engine untouched):
- Onset eased in: short qsin fade-in (8-14 ms) tames the attack transient
so pops bloop instead of snap (onset peak in first 20 ms down 5.6-8.7 dB
per cue; combo attack-to-peak 1.4-2.4 ms -> 15-26 ms).
- Highs rolled off: warm two-pole low-pass (2.6 kHz swap/lose, 3.0 kHz
match/combo, 3.6 kHz win) for a rounded tone. Spectral centroid down
~40-60%; >4 kHz energy collapses (win 76%->28%, combos ~10%->0.5%).
- Quieter: re-normalized to -15.5 dBFS (swap/lose -17.5), down from ~-9/-12,
lowering both peak and RMS on every cue.
Candy character retained; cascade ladder preserved (combo1..5 fundamentals
still ascend 1045<1173<1317<1566<1758 Hz). LICENSE provenance updated.
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.