Skip to content

Engine splash ("Made with Sparklight")

An optional startup screen a game turns on by data: the Sparklight mark assembles ray by ray, flares with a shock ring, sparks and a lens streak, the wordmark rises letter by letter under a light sweep, and a short sound sting plays. 2 to 4 seconds (3.2 by default), skippable, drawn on the GPU from the same polygons as the brand masters (assets/brand/), with no texture or font file.

Terminal window
# Turn it on in a game: an assets file (every key optional; `{}` is enough)
cat > assets/splash.json <<'JSON'
{ "enabled": true, "caption": "made_with", "language": "en", "duration_s": 3.2, "skippable": true, "sound": true, "volume": 0.8 }
JSON
# Look at it from an agent shell: agent runs hide it unless asked, and stay muted
spark run scenes/main.space3d.json --assets assets --gw-mute --splash --frames 240 --exit-after 30

The run’s summary line reports it: "splash": {"decision": "show", "frames": <drawn splash frames>, "skipped": false, "sting": false}.

run splash sting
a player’s run, splash.json with "enabled": true (or {}) shown plays (Ui group, volume)
no splash.json, or "enabled": false not shown (no_config, disabled) -
agent or test run: GAMEWAY_MUTE, --gw-mute, CLAUDECODE=1, an output-folder override, --autotest, --editor, an agent bundle id not shown (agent_run) -
the same with --splash shown never: an agent or test run is always silent
--no-splash never (off) -
--headless never (headless), even with --splash -

The sting is started only when the settings want it and the run is neither muted nor an agent or test run: splash::sting_audible checks the run’s StudioContext (mute, is_agent_run) and the process environment again (CLAUDECODE without GAMEWAY_ALLOW_SOUND=1), so an agent shell is silent whatever the context says. Such a run never decodes the sting or calls the audio engine for it. A skip fades the sting out over 0.25 s and stops it when the splash ends.

Schema: schemas/splash.schema.json. Unknown keys and values out of range are refused before any window opens (spark run exits 1): E_SPLASH_CONFIG (unreadable, not JSON, unknown key, wrong type), E_SPLASH_VALUE (duration_s outside 2 to 4, volume outside 0 to 1).

key default meaning
enabled true show it at start
caption made_with made_with (“MADE WITH” / “СДЕЛАНО НА”), powered_by (“POWERED BY” / “РАБОТАЕТ НА”) or none
language en en or ru: the caption’s language (the wordmark is never translated)
duration_s 3.2 length in seconds, 2 to 4
skippable true any key, mouse button or touch skips it after the first 0.3 s (the launch key does not); a gamepad button too in a game loop that polls the gamepad (sparklight::play has no gamepad input)
sound true play the sting
volume 0.8 sting volume, 0 to 1

A game that lets the player pick a language passes it before the splash (it reads splash.json once at start; write the language into the file the game ships, or build the SplashConfig in code, below).

t what
0 - 0.25 fade in from black; ambient glow
0.15 - 1.15 the four main rays grow out of the centre (N, E, S, W), then the four diagonal rays; the mark turns into place
0.31 the sting starts; its impact (0.64 s into the file, measured) lands on the hit
0.95 the hit: flare, shock ring, lens streak, sparks, a short flash
1.2 - 2.1 caption and wordmark letters rise into place, one after another
1.9 - 2.5 a light sweep runs over the wordmark
2.75 - 3.2 fade to black; the game’s first frame follows

A skip fades to black in 0.25 s and fades the sting out with it.

The splash follows the renderer’s QualityTier: Low draws the logo, the ambient glow and the flare only; Medium adds the shock ring, the flash and the light sweep (10 sparks); High adds the lens streak (28 sparks); Ultra 48 sparks and a finer ring. Its size comes from the window in logical points (mark height min(0.36 h, 0.28 w)), so it fits landscape, portrait (phones) and ultra-wide windows with a 5 % margin.

Games with their own runner (not sparklight::play) use the same pieces:

use sparklight::splash::{self, Splash, SplashConfig, SplashDecision, SplashOverride};
let config = SplashConfig::load(&assets)?; // Ok(None) without the file
let decision = splash::decide(config.as_ref(), &studio, SplashOverride::from_args(&args));
let mut splash = (decision == SplashDecision::Show).then(|| Splash::new(config.unwrap_or_default(), renderer.settings.tier));
// every frame while `splash` is Some:
let step = s.update(dt, any_key_or_button_or_touch_went_down);
if step.start_sting && splash::sting_audible(s.config(), &studio) { sting = Some(audio.play(&sting_sound, Group::Ui, s.config().volume, 0.0)); }
if let Some(h) = sting { audio.set_voice_volume(h, s.config().volume * step.sting_gain); }
let mut canvas = Canvas::new(w, h, scale_factor);
s.draw(&mut canvas);
overlay.draw_cleared(&mut encoder, &view, (w, h), &canvas, splash::BACKGROUND);
if step.finished { splash = None; } // draw the game from the next frame

splash::STING_OGG holds the sting (Sound::from_bytes). Gamepad buttons count as input when the game polls them and passes them to update. While the splash runs, sparklight::play keeps every press and touch from the game (releases still reach it), and a window that gets no redraws runs the splash out on the loop’s own ticks.

  • crates/sparklight-scene3d/tests/splash.rs: settings and schema (unknown keys refused), the visibility matrix above, the silent sting in every muted, agent or test run, the timeline (one sting, ends on time, skip, grace time, unskippable), a stalled frame, determinism (a frame depends only on time, size and tier), tiers, layout for seven window shapes, every glyph of every caption.
  • gpu_splash_key_frames_match_their_goldens, gpu_splash_portrait_frame_matches_its_golden (Mac, --profile needs): key frames at 0.5, 1.05, 1.6 and 2.3 s, Low tier in Russian, a phone portrait; goldens goldens/ui/gpu-splash-*.png.
  • crates/sparklight-cli/tests/play3d.rs: display_spark_run_shows_the_splash_only_when_asked_and_keeps_its_sting_silent (a real muted window), display_a_stalled_window_still_ends_the_splash_and_starts_the_game (no redraws at all) and spark_run_refuses_a_bad_splash_file_without_opening_a_window; the input routing of the player is unit-tested in play.rs (splash_input_tests).
  • Sound: crates/sparklight-scene3d/assets/splash/sparklight-sting.ogg, Seed Audio take mastered by Tools/audio/master-splash-sting.sh (4.0 s, stereo 48 kHz, -15.7 LUFS, true peak -1.3 dBFS); registry id sfx/SparklightSting/ogg.
  • Logo: the polygons and paths of assets/brand/sparklight-mark.svg and the wordmark (registry id ui/SparklightLogo/svg).