blob_flutter 1.1.0
blob_flutter: ^1.1.0 copied to clipboard
A highly customizable and performant 3D particle blob effect for Flutter.
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.

