Verified on 2026-09-14 with Flutter 3.44.2 and Dart 3.12.2.
sloshgen: 46 passing tests;dart analyzereports no issues.slosh: 30 passing tests plus the opt-in hybrid gallery;flutter analyzereports no issues.- Example: 2 passing widget tests;
flutter analyzereports no issues. - macOS profile-mode build and execution succeed.
Coverage includes required recipe fields and numeric limits, SVG command support and rejection, contour selection and concavity, curve extrema, unwrapped rotation, speed-dependent response, damping, area correction, empty/full fills, loop convergence/failure, deterministic bytes, compression, decoded output error, CLI/library equivalence and output preservation.
Flutter tests cover immutable decoding and malformed assets, shared dense reference agreement, endpoint/midpoint/seeking behavior, controls and ownership, asset replacement, TickerMode cleanup, stable CustomPaint across ticks, and pixel containment through rotated concave poses at 48 and 240 pixels. The example test switches scenes and scrolls to the playback controls.
dart run tool/regenerate.dart recreates all v1/v2 recipe outputs and the five v1 dense
numeric references. Dense references include independently integrated midpoint
samples at 480 Hz. No source path from the developer's machine is baked into an
asset. All example inputs are inside this repository.
| Scene | Layers | Retained frames | Encoded bytes | Max area error |
|---|---|---|---|---|
| Rectangle tilt | 1 | 27 | 18,481 | < 1e-12 |
| Quick turn | 1 | 29 | 19,730 | 0.000390 |
| Off-center pivot | 1 | 29 | 20,259 | 0.000633 |
| PathVerse single | 1 | 33 | 26,504 | 0.001782 |
| PathVerse layered | 3 | 33 | 67,124 | 0.002103 |
Area error is an absolute fraction of the vessel area, measured on interpolated
playback at validation samples. All scenes meet the requested 0.005 limit and
0.002 normalized surface-displacement tolerance. The layered loop's position
seam is 0.00000170 and its velocity seam is 0.000697 normalized units/second,
below 0.002 and 0.02 respectively. See fixtures/assets/*.report.json for exact
values and validation cadence. These are sampled bounds, not proofs over all
continuous time.
The rotation contact sheet samples 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 3.99 and 4 seconds. The observed surface stays approximately horizontal in presentation space while the vessel turns; the notch remains clear. Multiple slightly offset surfaces give a narrow translucent depth band. The final and initial poses agree visually, with a numeric velocity check as well. The live macOS player was inspected at enlarged size; pixel tests cover both icon and enlarged sizes. The model provides gentle stylized sloshing, not breaking waves or independently trapped liquid pools.
The gallery is reproducible with:
cd packages/slosh
flutter test --dart-define=RECORD_GALLERY=true test/gallery_test.dartOn macOS the recording test loads the installed Arial font for readable labels; that font is not copied or distributed in the repository. Elsewhere the image can use the Flutter test font. This affects labels, not liquid rendering.
Hardware: MacBook Pro (Mac16,1), Apple M4, 10 CPU cores, 16 GB RAM.
OS: macOS 26.6.2 (25G83). Profile mode, Flutter 3.44.2 / Dart 3.12.2.
The active Flutter view reported 60 Hz, a 16.67 ms frame interval.
Renderer: Skia using Metal. This toolchain's macOS desktop launcher sets
enable-impeller=false for platform-default operation; the generated app does
not override FLTEnableImpeller. The engine's macOS renderer uses a Metal
renderer configuration. No renderer override was supplied for this run.
Each case had two seconds of warm-up and six seconds of frame-timing collection. The harness repaints the liquid without rebuilding a control panel on each frame. Raw captured results and environment metadata are in profile-macos.json.
| Layers | Icons | Size | Samples | UI p50 / p95 (ms) | Raster p50 / p95 (ms) |
|---|---|---|---|---|---|
| 1 | 1 | 48 | 364 | 0.334 / 0.429 | 0.934 / 1.383 |
| 1 | 12 | 48 | 357 | 0.619 / 0.836 | 1.524 / 2.319 |
| 3 | 1 | 48 | 357 | 0.366 / 0.467 | 0.986 / 1.312 |
| 3 | 12 | 48 | 363 | 0.631 / 1.055 | 1.404 / 2.400 |
| 3 | 1 | 240 | 356 | 0.349 / 0.468 | 1.197 / 1.689 |
Every measured p95 UI and raster duration is below the active display's frame interval. This is one short desktop measurement, not an FPS guarantee for phones, web, busy applications or other renderers. Android/iOS/web runtime performance and total decoded heap usage were not measured.
The single-layer asset is 26,504 encoded bytes and has 10,528 bytes of decoded numeric payload; three layers use 67,124 and 27,424 bytes respectively. The numeric figures count coordinates and samples at eight bytes each. They exclude VM object/list overhead, strings, paints, paths and GPU resources and must not be reported as total heap memory. Multiple players can share the immutable asset; each adds its own interpolation buffers and rendering objects.
Reproduce from packages/slosh/example:
flutter run -d macos --profile --dart-define=SLOSH_PROFILE=trueThe SLOSH_PROFILE_DONE console line contains all eight cases (three v2 cases plus the five v1 comparisons). Profile another
target before making a performance claim about that target.
This correction replaces the previously reviewed launch effect. The model stays small: a pool waterline, rounded droplets, and attached/free wall state. Earlier parcel experiments and launch-based visual/performance acceptance are superseded.
Overview at 240/48 pixels · Wall carry, return, breakup and recombination
The clockwise rolling reference uses friction 0.85, adhesion 0.5 and a 1.4-second pickup cooldown. The first bead starts submerged at the rising left wall, travels upward while attached, releases at 0.692 s with its carried velocity, and returns to the pool (visible contact around 1.827 s, scalar absorption at 1.975 s). A second pickup starts at 1.4 s, so an amber bead remains visible after the first returns. Zero friction produces no pickups. Higher adhesion retains the bead farther around the wall. Counterclockwise motion lifts the right wall.
The combined shake uses x/y translation and angular reversals. It stretches, fragments and finally recombines around 2.612 s. Translation-only and rotation-only controls also split and merge; sustained spin and constant translation do not continuously add fragmentation strain. The low-fill bead settles, colored seeds merge using the documented style inheritance, and the supported PathVerse basin retains its pool. Pool and droplet colors remain independently configurable.
The rendered overview and event sides were inspected at both sizes; the live 240-pixel rolling animation and twelve 48-pixel icons were also inspected. The tiny breakup neck becomes subpixel at 48 pixels. Remesh boundaries preserve visual shape within the configured error budget. These are stylized circular beads, not wetting films or a physically calibrated fluid simulation.
| Scene | Encoded bytes | Retained frames | Max active vertices | Max visible area error | Max boundary error |
|---|---|---|---|---|---|
| rolling-circle | 1,892,375 | 375 | 384 | 0.788% | 0.003780 |
| violent-shake | 958,184 | 232 | 512 | 4.095% | 0.002370 |
| shake-translation | 791,476 | 185 | 512 | 4.107% | 0.003321 |
| shake-rotation | 581,645 | 180 | 256 | 3.141% | 0.003288 |
| droplet-bead | 150,554 | 56 | 128 | 1.258% | 0.000293 |
| droplet-split | 581,645 | 180 | 256 | 3.141% | 0.003288 |
| droplet-merge | 170,156 | 41 | 256 | 1.236% | 0.000321 |
| hybrid-pathverse | 58,703 | 21 | 128 | 1.330% | 0.000233 |
| rolling-circle-no-friction | 919,749 | 351 | 128 | 0.622% | 0.000337 |
Errors use normalized source distance. Rolling and shaking recipes use
quality.surface: 0.004; the other examples use 0.002. Each simple contour has
128 points. Scalar amounts are conserved within floating-point tolerance;
visible area is approximate and meets the configured 5% relative budget.
Geometry is checked at dense reference times, independent retained midpoints
and event sides, including simplicity, containment, overlap and correspondence.
Translation offsets are sampled alongside contours, motion knots survive
compression, and fixed swept bounds keep the fit scale stable.
Generator tests cover linear/angular velocity knots at 30/60/120 Hz, one-time strain injection, steady-motion controls, friction/adhesion comparisons, caps, amounts, styles, compact translated states, offset budgets and loop rejection. Player tests cover translated liquid/shell/clipping at pixel level, empty/full states, reverse seeking, incoming event selection, colors, holes and lifecycle. CLI/library parity and output preservation remain covered for both versions. All nine v2 fixtures regenerated byte-identically, and their playground copies match. The 15 historical v1 fixture hashes remain unchanged after regeneration.
Reproduce the images with:
cd packages/slosh
SLOSH_WRITE_GALLERY=true flutter test test/hybrid_gallery_test.dartM4 MacBook Pro, Flutter 3.44.2 / Dart 3.12.2, profile mode, Skia/Metal. The display reported 120 Hz. Each case has two seconds of warm-up and six seconds of samples. One-shot effects restart explicitly for measurement, without claiming loop safety. The profiling harness now initializes its controller before registering/starting callbacks; a regression test covers startup and restart. The clean run contains no reported framework errors. Full results and device metadata: profile-hybrid-macos.json.
| Rolling icons | Size | Frame reports | UI p50 / p95 (ms) | Raster p50 / p95 (ms) |
|---|---|---|---|---|
| 1 | 48 | 715 | 0.179 / 0.381 | 0.614 / 1.126 |
| 12 | 48 | 359 | 0.393 / 0.692 | 1.508 / 2.148 |
| 1 | 240 | 729 | 0.180 / 0.322 | 0.791 / 1.169 |
The twelve-icon case reported 359 frames over roughly six seconds; these timings do not establish sustained 120 fps. Startup asset validation/decoding is excluded. The rolling asset is 1,892,375 encoded bytes and 1,510,472 bytes of numeric payload, with at most 384 active vertices. Numeric payload excludes object/string and renderer overhead. Players share the decoded asset and keep bounded interpolation buffers. The asset is larger than a v1 waterline animation; playback remains interpolation and painting. Mobile and web performance remain unmeasured.