shader_legends_3 · AshleyGAnimNebula
Animated FBM Nebula with Chromatic Aberration
A multi-coloured, slowly rotating cosmic gas cloud built from 5 layers of FBM noise at different scales, each layer rotated by a different angle that varies with time. The R, G, B channels of the field are sampled with slightly different rotations and translations, producing a chromatic-aberration split that reads as soft purple/green halos. The combined scalar field is mapped to a colour via Inigo Quilez's cosine palette, with a sparse star layer and a soft vignette on top.
Original: https://www.shadertoy.com/results?query=ashleyg+nebula
▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.
Math essence
For each of 5 octaves k: rotate (x, y) by 0.4 t + 0.7 k, sample fbm(rotated * base_scale * step^k, 4 octaves), accumulate with weight 1 / (1 + 0.7 k). Compute the field three times for R, G, B with per-channel rotation/translation offsets of size chroma_strength. palette = IQ_cosine_palette(avg + palette_offset). final = palette * pow(tint, 0.6) + stars + background * (1 - brightness).
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 400 | 160–800 | Square output size in pixels. |
n_layers | int | 5 | 2–8 | Number of rotated FBM layers. |
base_scale | float | 1.2 | 0.5–8 | Sample scale of the lowest-frequency FBM layer. |
scale_step | float | 1.5 | 1.2–3 | Multiplicative step between consecutive layers. |
rot_speed | float | 0.05 | -0.3–0.3 | Per-step rotation rate of the FBM stack. |
chroma_strength | float | 0.1 | 0–0.3 | Per-channel chromatic-aberration offset magnitude. |
palette_a | rgb | 0.5,0.5,0.5 | null–null | Cosine palette a (bias). |
palette_b | rgb | 0.5,0.5,0.5 | null–null | Cosine palette b (amplitude). |
palette_c | rgb | 1,1,1 | null–null | Cosine palette c (frequency). |
palette_d | rgb | 0.1,0.33,0.67 | null–null | Cosine palette d (phase). |
palette_offset | float | 0.2 | 0–1 | Global hue offset into the palette. |
background | rgb | 0.02,0.01,0.06 | null–null | Deep-space background tint. |
star_threshold | float | 0.985 | 0.6–0.99 | Per-cell hash threshold above which a star is drawn. |
drift | float | 0.5 | 0–2 | Per-step time-advance multiplier. |
Reference
First 60 lines of reference.py. Full source:
library_external/shader_legends_3/AshleyGAnimNebula/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
def _hash21(x: np.ndarray, y: np.ndarray, seed: int) -> np.ndarray:
x = np.asarray(x, dtype=np.int64)
y = np.asarray(y, dtype=np.int64)
p = (x * np.int64(374761393) + y * np.int64(668265263) + np.int64(seed)) & np.int64(0xFFFFFFFF)
p = (p ^ (p >> np.int64(13))) * np.int64(1274126177) & np.int64(0xFFFFFFFF)
p = p ^ (p >> np.int64(16))
return (p & np.int64(0xFFFFFFFF)).astype(np.float32) / np.float32(0xFFFFFFFF)
def _value_noise_2d(x: np.ndarray, y: np.ndarray, seed: int) -> np.ndarray:
xi = np.floor(x).astype(np.int64)
yi = np.floor(y).astype(np.int64)
xf = (x - xi).astype(np.float32)
yf = (y - yi).astype(np.float32)
u = xf * xf * (3.0 - 2.0 * xf)
v = yf * yf * (3.0 - 2.0 * yf)
n00 = _hash21(xi, yi + seed, 0)
n10 = _hash21(xi + 1, yi + seed, 0)
n01 = _hash21(xi, yi + 1 + seed, 0)
n11 = _hash21(xi + 1, yi + 1 + seed, 0)
nx0 = n00 * (1.0 - u) + n10 * u
nx1 = n01 * (1.0 - u) + n11 * u
return nx0 * (1.0 - v) + nx1 * v
def _fbm(x: np.ndarray, y: np.ndarray, octaves: int, seed: int, lac: float = 2.0, gain: float = 0.5) -> np.ndarray:
total = np.zeros_like(x, dtype=np.float32)
amp = np.float32(1.0)
freq = np.float32(1.0)
norm = np.float32(0.0)
for k in range(octaves):
total = total + amp * _value_noise_2d(x * freq, y * freq, seed + k * 17)
norm = norm + amp
amp = amp * np.float32(gain)
freq = freq * np.float32(lac)
return total / max(norm, 1e-6)
def _rot2d(x: np.ndarray, y: np.ndarray, ang: float) -> tuple[np.ndarray, np.ndarray]:
c = np.cos(ang)
s = np.sin(ang)
return c * x - s * y, s * x + c * y
class AshleyGAnimNebula:
"""Animated Nebula — Ashley G. (Shadertoy user 'ashleyg').
Ashley G's *Nebula* is a chromatic-aberration-on-FBM shader that
produces a slowly rotating, multi-coloured cosmic gas cloud.
The recipe:
1. **Five FBM layers** at different scales, each rotated by a
different angle that varies with time. The layers are Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/shader_legends_3/AshleyGAnimNebula/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from shader_legends_3
Atmospheric Perspective Landscape
shader_legends_3 · HubertyAtmospheric
A painterly 2D landscape built from depth-graded FBM bands: a soft sky gradient with a sun disc and cloud streaks, a low-amp haze band in the upper-middle, rolling green midground hills, and a sharp foreground silhouette of trees and grass.
Bokeh Blur with Light Leaks
shader_legends_3 · TaterBokeh
A 2D bokeh effect built by stamping 50-200 soft Gaussian discs onto a near-black backdrop, each at a hash-determined centre, radius, and warm-to-cool colour.
Hash-driven Procedural City Skyline
shader_legends_3 · DaveHoskinsHashCity
A 2D orthographic night-time cityscape whose per-column building heights come from 1D FBM noise and whose lit windows are a per-cell hash threshold.
Recursive Spherical Displacement Wireframe
shader_legends_3 · IQDisplacementSphere
A wireframe sphere whose surface is recursively displaced by spherical harmonics of the form amp * sin(N * theta) * cos(M * phi), rendered as overlapping latitude and meridian polylines plus random surface dots.
Volumetric Clouds from Cellular + FBM
shader_legends_3 · BigWingsClouds
A painterly cumulus-cloud sky built from a Worley/cellular noise layer (cloud puffs) gated by a low-frequency FBM (large-scale cloud coverage) and a high-frequency FBM (in-cloud detail).