Recreating Stunning Strange Attractor, Fibonacci Sphere & Galaxy Animations in Flutter with Pure Dart
This article explains how to implement three complex visual effects—Strange Attractor, Fibonacci Sphere, and Galaxy animations—in Flutter using only Dart code, covering the underlying differential equations, Euler integration, 3D‑to‑2D projection, rotation, perspective, performance optimizations, and solutions to common GPU tile‑artifact issues.
Overview
This article demonstrates how to implement three mathematically‑driven visual effects in Flutter using pure Dart: a Strange Attractor animation, a Fibonacci Sphere, and a Galaxy (star‑nebula) scene. All effects rely on differential equations, geometric algorithms, and GPU‑accelerated rendering.
Strange Attractor Animation
The animation visualises classic chaotic systems by integrating their differential equations with Euler integration and projecting the resulting 3D particle positions onto the 2D Canvas.
Key Steps
Mathematical model : Convert the attractor’s differential equations into Dart code and update particle velocity with Euler integration in a method such as _updatePhysics.
3D‑to‑2D projection : Apply a simple perspective projection (u, v) = f(x, y, z) and rotate the coordinate system to give depth.
newPosition = oldPosition + velocity(dx, dy, dz) * dt
Halvorsen Attractor
const a = 1.4;
dx = -a * p.x - 4 * p.y - 4 * p.z - (p.y * p.y);
dy = -a * p.y - 4 * p.z - 4 * p.x - (p.z * p.z);
dz = -a * p.z - 4 * p.x - 4 * p.y - (p.x * p.x);Lorenz Attractor
dx = sigma * (p.y - p.x);
dy = p.x * (rho - p.z) - p.y;
dz = p.x * p.y - beta * p.z;Aizawa Attractor
dx = (p.z - b) * p.x - d * p.y;
dy = d * p.x + (p.z - b) * p.y;
dz = c + a * p.z - (p.z * p.z * p.z) / 3 - (p.x * p.x + p.y * p.y) * (1 + e * p.z) + f * p.z * (p.x * p.x * p.x);Sprott B Attractor
dx = a * p.y * p.z;
dy = p.x - p.y;
dz = b - p.x * p.y;Stability Fixes
Boundary check (e.g., if (p.x.abs() > 50 || p.x.isNaN) …)
Respawn logic ( _resetSingleParticle(p))
Reduced time step (e.g., dt = 0.004 for Halvorsen)
Fibonacci Sphere Animation
Points are distributed uniformly on a sphere using the golden‑angle algorithm, then the sphere is rotated and rendered with perspective.
Algorithm
final double goldenAngle = pi * (3 - sqrt(5)); // ~137.5°
for (int i = 0; i < numPoints; i++) {
double y = 1 - (i / (numPoints - 1)) * 2; // uniform y
double radiusAtY = sqrt(1 - y * y);
double theta = goldenAngle * i;
double x = cos(theta) * radiusAtY;
double z = sin(theta) * radiusAtY;
// apply rotation matrix, then perspective
}Rotation & Perspective
double x1 = x * cos(rotationY) - z * sin(rotationY);
double z1 = x * sin(rotationY) + z * cos(rotationY);
double focalLength = 800.0;
double perspective = focalLength / (focalLength - pz);
double screenX = center.dx + px * perspective;
double screenY = center.dy + py * perspective;
double pointSize = basePointSize * perspective;Visual Enhancements
Wobble wave using sin(time) and vertical phase shift + y * 4.
Trails: draw circles when trails == 0, draw lines when trails > 0 with length scaled by perspective.
Galaxy (Star‑Nebula) Animation
Extends the Sprott B attractor with additional forces to simulate a galaxy‑like structure.
Physical Model
Central attraction provides a basic centripetal force.
Rotating‑rod force creates a bipolar field.
Accretion disc: particles spawn on an outer ring and lose energy via velocity *= 0.9995.
Turbulence Function
Offset getTurbulence(int i, double phase, double t) {
double speed = 2.0;
double dx = 0.06 * sin(t * speed + phase) + 0.03 * cos(t * speed * 2.3 + i * 0.1);
double dy = 0.06 * cos(t * speed * 1.5 + phase) + 0.03 * sin(t * speed * 1.9 + i * 0.1);
double rotAngle = t * 0.5 * (i % 2 == 0 ? 1 : -1);
double rx = dx * cos(rotAngle) - dy * sin(rotAngle);
double ry = dx * sin(rotAngle) + dy * cos(rotAngle);
return Offset(rx, ry);
}Rendering Optimizations
Store particle data in Float32List for compact memory layout.
Batch‑draw 30 000 particles with canvas.drawRawPoints using BlendMode.plus to create bright overlapping regions.
Tile‑Artifact Problem & Workarounds
When blending a gradient from orange to transparent, uninitialized GPU tiles produced dark squares. Attempts such as TileMode.decal, adjusting transparent colors, and switching BlendMode (Screen/Plus) did not eliminate the artifacts. The final practical workaround layered multiple phase‑shifted wobble animations, varying size and scale over time, which avoided the problematic gradient while preserving the glowing effect.
Resources
Full source code: https://github.com/CarGuo/gsy_flutter_demo
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
