Blob Flutter (3D Particle Blob)
A high-performance, interactive 3D particle blob for Flutter.
Powered by procedural noise algorithms, multi-threaded Isolate computation, and GPU Fragment Shaders.
Live Demo • Features • What's New • Quick Start • Algorithms • Controller • Error Handling • Architecture
Features
- Zero-Jank Architecture: Heavy 3D math and vertex projections run in a background
Isolatefor sustained 60/120 FPS. - Hardware-Accelerated Shaders: High-performance GPU fragment shaders for fluid color gradients and shimmer effects.
- 9 Procedural Noise Models: Smooth waves, spiky crystals, cosmic vortex, cellular bubbles, carpet nets, and custom math formulas.
- Fluid Touch Interaction: Natural drag rotation, mouse hover tracking, and tap dispersion with customizable hit testing.
- Zero-Allocation Pipeline: Pre-allocated typed buffers prevent Garbage Collection (GC) pauses during animation.
- Single GPU Draw Call: Flattens and renders thousands of particles in a single call via
Canvas.drawRawPoints.
What's New
- Real 3D Colors (
uColor3D): Shader gradients now rotate and turn with the 3D blob in real-time instead of staying fixed like a flat 2D wallpaper. - Depth Sorting (
enableDepthSort): Back-to-front $O(N)$ sorting ensures front particles properly cover rear ones, removing visual glitching. - Atmospheric Depth-Cueing (
enableDepthCueing): Distant particles scale down and softly fade, creating authentic depth and volume. - Responsive Auto-Fit (
autoFit): Automatically resizes the blob to fit any screen, container, or device orientation without manual pixel calculations. - Fast Web Performance (
isComplex): Alternates calculation frames on Flutter Web to keep single-threaded JavaScript smooth and responsive. - Smart Battery Saver: Automatically sleeps (0% CPU & GPU) when scrolled offscreen, when the app is minimized, or when navigating to another page.
Quick Start
1. Install
flutter pub add blob_flutter
2. Import
import 'package:blob_flutter/blob_flutter.dart';
3. Use
The simplest way to render a basic Blob:
// 1. Basic Blob with Fixed Radius
BlobFlutter(
particleCount: 5000,
radius: 150.0,
pointSize: 2.0,
noiseType: BlobNoiseType.harmonic,
gradient: const LinearGradient(
colors: [Colors.cyanAccent, Colors.purpleAccent],
),
)
// 2. Fully Responsive Blob with 3D Depth Sorting & Depth-Cueing
BlobFlutter(
particleCount: 5000,
autoFit: true, // Automatically resizes radius to fit parent bounds
radiusFactor: 0.85,
enableDepthSort: true, // Linear O(N) back-to-front depth sorting
enableDepthCueing: true, // Realistic atmospheric perspective attenuation
noiseType: BlobNoiseType.harmonic,
gradient: const LinearGradient(
colors: [Colors.cyanAccent, Colors.purpleAccent],
),
)
Controller Usage
For dynamic runtime control, use the BlobController. It allows you to morph geometry, change colors, and tweak physics on the fly.
class MyBlob extends StatefulWidget {
@override
_MyBlobState createState() => _MyBlobState();
}
class _MyBlobState extends State<MyBlob> {
late BlobController _controller;
@override
void initState() {
super.initState();
_controller = BlobController(
particleCount: 5000,
radius: 150.0,
noiseType: BlobNoiseType.simplex,
dampingFactor: 0.95,
isColorAnimated: true,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTap: () => _controller.setNoiseType(BlobNoiseType.spiky),
child: BlobFlutter(
controller: _controller,
),
);
}
}
Tip
Controller Precedence & Single Source of Truth:
When an external BlobController is provided to BlobFlutter, the controller serves as the single source of truth. Passing widget-level configuration properties (particleCount, radius, pointSize, speed, noiseType, gradient, etc.) alongside controller will be safely ignored with a debug-mode warning. Always configure those properties directly inside BlobController(...).
Procedural Noise Algorithms
Choose from 9 distinct mathematical displacement models using the BlobNoiseType enum:
| Algorithm | Visual Characteristics | Best For |
|---|---|---|
harmonic |
Smooth, organic, fluid liquid blob motion. | Liquid effects, calm assistants |
spiky |
Sharp peaks, crystalline spikes, urchin geometry. | Audio visualizers, energetic UI |
fractal |
Multi-octave turbulent cloud and terrain details. | Complex, textured surfaces |
cellular |
Segmented clusters, biological cells, bubbles. | Organic, microscopic visuals |
vortex |
Swirling cyclone, spiral galaxy, tornado. | Loading spinners, portals |
sphericalHarmonics |
Acoustic cymatics, nodal patterns, quantum fields. | High-tech, futuristic UI |
simplex |
Omni-directional, artifact-free smooth flow. | Clean, continuous deformation |
wave |
Flat full square carpet/net with undulating wave ripples. | Floating wave nets, square carpets, audio grids |
custom |
User-defined mathematical procedural noise algorithm. | Custom 3D shapes, stars, toruses, hearts, custom math |
Custom Procedural Noise & 3D Math Engine
With BlobNoiseType.custom, you have complete creative freedom to sculpt custom 3D geometries, pulsating crystals, hollow toruses, swirling spirals, or bespoke mathematical motions.
1. Custom Noise Function Signature
A custom noise function evaluates per-particle displacement in 3D space:
typedef BlobCustomNoiseFunction = double Function(
double px, // Particle X on unit sphere (-1.0 to 1.0)
double py, // Particle Y on unit sphere (-1.0 to 1.0)
double pz, // Particle Z on unit sphere (-1.0 to 1.0)
double frequency, // noiseFrequency parameter
double time, // Animation time in seconds
double blobiness, // Global deformation multiplier
);
2. Built-in Math Helpers in BlobMath
BlobMath provides high-performance, allocation-free static utilities for sculpting 3D particles:
BlobMath.azimuth(px, pz): Azimuthal angle $\phi = \text{atan2}(pz, px) \in-\pi, \pi$ (ideal for longitude/spiral twisting).BlobMath.elevation(py): Elevation angle $\theta = \text{asin}(py) \in-\pi/2, \pi/2$ with safe clamping againstNaN.BlobMath.distance2D(x, z): Planar radial distance $\sqrt{x^2 + z^2}$ from the Y-axis.BlobMath.fastSimplex3D(x, y, z): High-speed Simplex 3D noise for organic, terrain-like surfaces.BlobMath.smoothstep(edge0, edge1, x): Hermite interpolation for smooth borders and transitions.BlobMath.clampDisplacement(val): Automatic safeguard againstNaNorInfinity, clamping values safely to[0.05, 5.0].
3. Usage Examples
Via BlobController:
final controller = BlobController(
noiseType: BlobNoiseType.custom,
customNoise: (px, py, pz, f, time, blobiness) {
final double phi = BlobMath.azimuth(px, pz);
return 1.0 + sin(phi * 4.0 + time) * 0.3 * blobiness;
},
);
// Switch or update dynamically at runtime:
controller.setCustomNoise((px, py, pz, f, time, blobiness) {
return 1.0 + BlobMath.fastSimplex3D(px * f, py * f, pz * f + time) * 0.35 * blobiness;
});
Via BlobFlutter Widget:
BlobFlutter(
noiseType: BlobNoiseType.custom,
customNoise: (px, py, pz, f, time, blobiness) {
final double r = BlobMath.distance2D(px, pz);
return 1.0 + cos(r * 8.0 * f - time * 3.0) * 0.25 * blobiness;
},
)
4. Golden Rules for Glitch-Free Shapes
- The 1.0 Anchor: Displacements scale the unit sphere. Always write equations relative to
1.0(e.g.1.0 + (wave * blobiness)). - Safe from NaN & Isolate Closures: Custom noise closures run synchronously on the main thread (<0.5ms for 3,000 particles), allowing you to write any lambda or closure without Isolate serialization errors. All returns are automatically protected from
NaNandInfinity. - Zero Heap Allocations: The function is invoked per-particle every frame. Keep all calculations on
doubleprimitives without instantiating objects or collections.
5. Practical Shape Recipes
// 1. Classic 5-Point 3D Star
controller.setCustomNoise((px, py, pz, f, time, blobiness) {
final double angle = atan2(py, px) + time * 0.4;
final double star2D = max(0.0, cos(5.0 * angle));
final double sharpPoints = pow(star2D, 2.5).toDouble();
final double zProfile = max(0.0, 1.0 - pz.abs() * 2.0);
return (0.45 + sharpPoints * zProfile * 1.5) * blobiness;
});
// 2. Saturn Planet & Glowing Equatorial Ring
controller.setCustomNoise((px, py, pz, f, time, blobiness) {
final double yDist = py.abs();
final double ring = yDist < 0.15 ? pow(0.9 - yDist / 0.35, 5.0).toDouble() * 1.5 : 0.0;
return (0.55 + ring) * blobiness;
});
Customization Properties
Widget Properties (BlobFlutter)
Configure the initial state of your blob directly in the widget.
| Property | Type | Default | Description |
|---|---|---|---|
particleCount |
int |
5000 |
Total number of particles on the sphere (higher counts increase density but may affect performance). |
radius |
double |
150.0 |
Base radius in logical pixels. |
autoFit |
bool |
false |
Dynamically resizes the radius to fit parent container bounds (min(w, h) / 2.0) * radiusFactor. |
radiusFactor |
double |
0.85 |
Multiplier applied to half the minimum container dimension when autoFit is active (leaves room for wave crests). |
enableDepthSort |
bool |
true |
Linear $O(N)$ bucket sort in isolate; renders particles back-to-front (Painter's algorithm) to prevent depth artifacts. |
enableDepthCueing |
bool |
true |
Dynamic perspective scaling and opacity attenuation across depth slices for authentic 3D realism. |
depthCueingFactor |
double |
0.4 |
Intensity of depth-cueing perspective scaling ([0.0, 1.0]). |
pointSize |
double |
2.0 |
Diameter of each rendered particle. |
rotationX / rotationY |
double |
0.0 |
Initial base 3D orientation angles (pitch & yaw) in radians. |
noiseType |
Enum |
harmonic |
Procedural 3D noise algorithm used. |
customNoise |
BlobCustomNoiseFunction? |
null |
Custom procedural displacement function used when noiseType is BlobNoiseType.custom. |
controller |
BlobController? |
null |
External controller for runtime manipulation. |
gradient |
Gradient |
Linear |
Color gradient (Linear, Radial, or Sweep) mapped in 3D object space. |
isComplex |
bool |
false |
Enables temporal frame interleaving (striding) across alternating frames to halve per-frame CPU calculations. |
webTemporalInterleaving |
bool |
true |
Automatically enables frame interleaving on Flutter Web to guarantee smooth 60 FPS. |
maxWebParticles |
int? |
3000 |
Automatic particle cap on Flutter Web to safeguard single-threaded JS performance. |
autoPlay |
bool |
true |
Whether the animation loop starts automatically. Set to false for battery savings on static views or widget tests. |
autoPauseOffscreen |
bool |
true |
Automatically halts ticker and isolate (0% CPU/GPU) when scrolled out of viewport. |
autoPauseOnAppBackground |
bool |
true |
Automatically pauses when application is minimized, hidden, or in the background. |
autoPauseOnRouteChange |
bool |
true |
Automatically pauses when navigated away from via Navigator routes (ModalRoute). |
routeObserver |
RouteObserver? |
null |
Optional observer for navigation route transitions. |
interactive |
bool |
true |
When false, touches pass through seamlessly to underlying widgets in a Stack. |
hitTestBehavior |
HitTestBehavior |
translucent |
Hit-test event dispatching behavior (translucent, opaque, deferToChild). |
Tip
Performance & Particle Count (particleCount):
Increasing the particle count enhances visual fullness and detail, but directly increases computation time in the isolate and vertex drawing load on the GPU:
- 1,000 – 3,000: Ideal for low-end devices, battery-sensitive apps, or subtle background elements.
- 3,000 – 6,000 (Default:
5000): Sweet spot for smooth 60/120 FPS on most modern mobile devices. - 8,000 – 20,000+: Recommended for modern flagship phones, desktop, or web applications with capable GPUs.
(Note: These figures are approximations and may vary depending on target device hardware and workload).
Controller Properties (BlobController)
Manipulate the blob dynamically at runtime using the controller methods.
| Setter Method | Valid Range / Type | Description |
|---|---|---|
pause() |
- | Stops the animation ticker completely (0% CPU/battery usage). |
resume() |
- | Resumes the animation loop if paused. |
isPaused |
true/false |
Getter checking whether the animation loop is currently paused. |
setParticleCount(val) |
10 - 100000 |
Dynamically sets particle count (reallocates buffers). |
setAutoFit(val) |
true/false |
Toggles dynamic viewport-fitting radius. |
setRadiusFactor(val) |
double |
Changes container dimension multiplier in auto-fit mode. |
setEnableDepthSort(val) |
true/false |
Toggles isolate $O(N)$ Z-depth sorting (Painter's algorithm). |
setEnableDepthCueing(val) |
true/false |
Toggles atmospheric perspective depth scaling. |
setDepthCueingFactor(val) |
0.0 - 1.0 |
Adjusts depth attenuation strength. |
setIsComplex(val) |
true/false |
Enables/disables temporal frame interleaving (striding). |
setWebTemporalInterleaving(val) |
true/false |
Toggles temporal frame interleaving on Web. |
setMaxWebParticles(val) |
int? |
Sets or clears web particle count limit. |
setBlobiness(val) |
0.0 - 5.0 |
Amplitude of noise displacement. |
setSpeed(val) |
0.0 - 10.0 |
Playback speed of the animation. |
setRotationX(val) / setRotationY(val) |
double (radians) |
Sets persistent 3D orientation pitch & yaw angles. |
setRotation({x, y}) |
double? (radians) |
Sets both 3D orientation angles simultaneously. |
setDispersion(val) |
0.0 - 3.0 |
Outward radial displacement. |
setNoiseFrequency(val) |
0.1 - 5.0 |
Density of the noise ripples. |
setNoiseType(type) |
Enum |
Changes the deformation algorithm. |
setCustomNoise(fn, {switchToCustom}) |
Function? |
Sets custom procedural noise callback and optionally sets noiseType to custom. |
setIsRainbowMode(bool) |
true/false |
Cycles colors through the HSV spectrum. |
zoomIn(val) / zoomOut |
- | Scales the blob size dynamically. |
(Check the source code for a complete list of advanced physics and shader properties).
Architecture & Performance
BlobFlutter is built with deep respect for both developers and end-user hardware. Every mathematical model, buffer allocation, and render pass is calculated with exacting precision to deliver sustained 60 / 120 FPS while safeguarding device resources, thermals, and battery life:
- Persistent Worker Isolate: 3D math, trigonometric deformations, and matrix rotations execute in a dedicated background worker (
BlobWorker). The UI receives data via zero-copyTransferableTypedData. - True 3D Object-Space Fragment Shaders (
blob.frag): Rather than projecting flat 2D color coordinates across screen pixels, the GLSL fragment program reconstructs the 3D surface normal $\vec{n} = (\hat{p}_x, \hat{p}_y, \hat{p}_z)$ for each point and multiplies by the inverse pitch/yaw rotation matrix ($R^T$). Colors rotate synchronously with the 3D object in space. - $O(N)$ Linear Depth Bucket Sorting: To preserve 60/120 FPS without Garbage Collection pauses, the worker isolate sorts particles using a 64-bin linear bucket sort ($O(N)$ vs $O(N \log N)$ quicksort). Combined with 4-strata atmospheric depth-cueing in
BlobPainter, particles render back-to-front with depth-dependent scale and opacity. - Single GPU Draw Call: Particle coordinates are flattened and drawn directly to graphics hardware using
Canvas.drawRawPoints. - Zero Heap Allocation: Coordinate caches, depth sorting bins, and calculation buffers are pre-allocated during initialization, avoiding Garbage Collector (GC) stutters.
- Web Temporal Striding (
isComplex): On Flutter Web, where Dart executes on a single JavaScript event loop, temporal interleaving splits particle computations across alternating odd/even frames, reducing CPU execution time by 50% without visual stutter. - Ultra-Fast Path Engine: During steady-state animations (when no pointers or radial dispersions are active), the math loop transitions into an unbranched, streamlined execution path, eliminating redundant pointer and touch checks per frame.
- Multi-Tier Zero-Battery Lifecycle: Tickers and isolate computations automatically shut down (0% CPU/GPU usage) when scrolled offscreen, when the app enters the background, or when navigating to another route via
ModalRoute/RouteObserver.
Note
Performance Scaling: Although computation is offloaded to a background Isolate to keep the UI thread jank-free, mathematical transformations and GPU vertex throughput scale linearly with particleCount. Very high counts on budget or older hardware may impact frame rates or cause battery drain.
Error Handling
BlobFlutter provides actionable console diagnostics with automatic CPU fallback if shaders are unavailable.
Catch issues programmatically or render custom fallback interfaces via onError and errorBuilder.
Libraries
- blob_flutter
- A high-performance interactive 3D particle blob effect for Flutter.
