Pine mist
inspired by Olly Moss, Firewatch1
Five pine-crested ridges fade into mist, and a sun sinks halfway behind the farthest, bare hill.
Made with Claude Opus 5.5
- Technique
- flat shapes
- Shape
- Any screen
- Added
- 27 September 2026
Colours
- #1C1B1Abackground
- #DAD8CEforeground
- #CF6A4Caccent
Export
FormatThis browser can’t make WebP files.
Shapecropped from 16:9
Crop
SizeThis browser can’t draw a file that large.
Notes
Each ridge is one flat shape: a crest traced from smooth noise, merged with its pines and shaded lighter towards its foot, where mist gathers before the next ridge. A coarser noise decides where pines grow, and any stand of fewer than five trees is cleared. The stepped, flat layers follow Olly Moss’s key art for Firewatch (2016).
Sources
- Olly Moss, Firewatch, 2016. ↑
- Aerial perspective.
Source code
wallpapers/pine-mist/design.py, 120 lines
"""Pine-forest ridges receding into fog, a sun sinking behind the farthest hill, in flat noise-cut layers."""
import numpy as np
from numpy.typing import NDArray
from shapely.geometry import Polygon
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union
from walldye import (
ACCENT,
ACCENT_2,
ACCENT_7,
BG,
BG_ALT,
BG_DEEP,
BLACK,
UI,
UI_ALT,
Canvas,
P,
Rng,
by_regime,
design,
mix,
)
from walldye.field import runs
SUN_X, SUN_R = 560, 72 # the terrain is laid out around the sun as it sits at 16:9
# (base y, ridge amplitude, tree height, tree spacing, tone) far → near, on a 1080-tall canvas;
# the far hill is bare. Nearer ridges sink below BG on dark themes and take more ink on light ones.
RIDGES = (
(600, 60, 0, 0, by_regime(mix(BG_ALT, UI, 0.55), mix(BG_ALT, UI, 0.15))),
(665, 80, 28, 10, by_regime(mix(BG_ALT, UI, 0.1), mix(BG_ALT, UI, 0.45))),
(745, 100, 42, 13, by_regime(mix(BG, BG_ALT, 0.45), mix(BG_ALT, UI, 0.75))),
(840, 115, 62, 18, by_regime(BG_DEEP, mix(UI, UI_ALT, 0.2))),
(960, 120, 92, 26, by_regime(mix(BG_DEEP, BLACK, 0.5), mix(UI, UI_ALT, 0.5))),
)
SKY = mix(BG, UI, 0.35)
MIST = mix(BG_ALT, UI, 0.5)
def pine(x: float, y: float, h: float, r: Rng) -> BaseGeometry:
"""Jagged three-tier conifer outline standing on (x, y)."""
w = h * r.uniform(0.2, 0.26)
right = [(x, y - h)]
for k, (ty, tw) in enumerate([(0.36, 0.5), (0.66, 0.78), (0.97, 1.0)]):
tw *= r.uniform(0.85, 1.1)
right.append((x + w * tw, y - h + h * ty))
if k < 2:
right.append((x + w * tw * 0.42, y - h + h * (ty - 0.04)))
right.append((x + w * 0.2, y + 4))
left = [
(2 * x - px + r.uniform(-0.6, 0.6), py + r.uniform(-1.5, 1.5))
for px, py in reversed(right[1:])
]
return Polygon(right + left).buffer(0)
@design(aspects="any")
def draw(s: Canvas) -> None:
n, r = s.noise(21), s.rng(21)
sun_x = s.pick(landscape=(SUN_X / 1920, 0), portrait=(0.32, 0)).x
ox = sun_x - SUN_X # terrain is sun-relative, so the hill under the sun never changes
# Portrait screens stretch the ridges' depth so the land still fills the lower part.
k = max(1.0, 0.72 * s.h / 1080)
xs = np.arange(-20, s.w + 21, 8, dtype=float)
def base_y(base: float) -> float:
return s.h - k * (1080 - base)
def crest(i: int, base: float, amp: float) -> NDArray[np.float64]:
u = xs - ox
bump = 0.25 * amp * np.exp(-(((u - SUN_X) / 380) ** 2))
return base_y(base) + k * (2 * amp * n.fbm(u / 900, i * 3.1 + 0.5, 3) + bump)
def ridge(i: int, base: float, amp: float, th: float, gap: float) -> BaseGeometry:
ys = crest(i, base, amp)
shapes: list[BaseGeometry] = [
Polygon([*zip(xs, ys), (s.w + 20, s.h + 20), (-20, s.h + 20)])
]
if th:
walk: list[float] = []
x = -20.0
while x < s.w + 20:
walk.append(x)
x += gap * r.uniform(0.7, 1.2)
# Forest in loose stands: a coarse noise mask, short runs dropped so no tree stands alone.
wood = n((np.array(walk) - ox) / 240, i * 7.7) > -0.05
for a, b in runs(wood):
if b - a < 5:
continue
for x in walk[a:b]:
y = float(np.interp(x, xs, ys))
shapes.append(pine(x, y + th * 0.25, th * r.uniform(0.7, 1.2), r))
return unary_union(shapes).simplify(0.3)
hz = base_y(RIDGES[0][0])
s.fill(
P().rect(0, 0, s.w, s.h),
s.linear_gradient([(0, BG), (1, SKY)], (0, hz - 222 * k), (0, hz + 48 * k)),
)
# Sink the sun so ~45% of it sits behind the bare far hill.
base0, amp0 = RIDGES[0][0], RIDGES[0][1]
cy = float(np.interp(sun_x, xs, crest(0, base0, amp0))) - 0.08 * SUN_R
sun = (sun_x, cy)
for f, op in ((2.6, 0.05), (1.9, 0.07), (1.4, 0.09)):
s.fill(P().circle(sun, f * SUN_R), ACCENT_7, opacity=op)
s.fill(
P().circle(sun, SUN_R),
s.linear_gradient([(0, ACCENT), (1, ACCENT_2)], (0, cy - SUN_R), (0, cy + SUN_R)),
)
for i, (base, amp, th, gap, tone) in enumerate(RIDGES):
# Mist pools in the band the next ridge leaves visible, so each layer's foot lifts off the one in front.
nxt = base_y(RIDGES[i + 1][0]) if i + 1 < len(RIDGES) else s.h
fill = s.linear_gradient(
[(0, tone), (1, mix(tone, MIST, 0.35))],
(0, base_y(base) - 0.3 * amp * k),
(0, nxt),
)
s.fill(P().shape(ridge(i, base, amp, th, gap)), fill)"""Pine-forest ridges receding into fog, a sun sinking behind the farthest hill, in flat noise-cut layers."""
import numpy as np
from numpy.typing import NDArray
from shapely.geometry import Polygon
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union
from walldye import (
ACCENT,
ACCENT_2,
ACCENT_7,
BG,
BG_ALT,
BG_DEEP,
BLACK,
UI,
UI_ALT,
Canvas,
P,
Rng,
by_regime,
design,
mix,
)
from walldye.field import runs
SUN_X, SUN_R = 560, 72 # the terrain is laid out around the sun as it sits at 16:9
# (base y, ridge amplitude, tree height, tree spacing, tone) far → near, on a 1080-tall canvas;
# the far hill is bare. Nearer ridges sink below BG on dark themes and take more ink on light ones.
RIDGES = (
(600, 60, 0, 0, by_regime(mix(BG_ALT, UI, 0.55), mix(BG_ALT, UI, 0.15))),
(665, 80, 28, 10, by_regime(mix(BG_ALT, UI, 0.1), mix(BG_ALT, UI, 0.45))),
(745, 100, 42, 13, by_regime(mix(BG, BG_ALT, 0.45), mix(BG_ALT, UI, 0.75))),
(840, 115, 62, 18, by_regime(BG_DEEP, mix(UI, UI_ALT, 0.2))),
(960, 120, 92, 26, by_regime(mix(BG_DEEP, BLACK, 0.5), mix(UI, UI_ALT, 0.5))),
)
SKY = mix(BG, UI, 0.35)
MIST = mix(BG_ALT, UI, 0.5)
def pine(x: float, y: float, h: float, r: Rng) -> BaseGeometry:
"""Jagged three-tier conifer outline standing on (x, y)."""
w = h * r.uniform(0.2, 0.26)
right = [(x, y - h)]
for k, (ty, tw) in enumerate([(0.36, 0.5), (0.66, 0.78), (0.97, 1.0)]):
tw *= r.uniform(0.85, 1.1)
right.append((x + w * tw, y - h + h * ty))
if k < 2:
right.append((x + w * tw * 0.42, y - h + h * (ty - 0.04)))
right.append((x + w * 0.2, y + 4))
left = [
(2 * x - px + r.uniform(-0.6, 0.6), py + r.uniform(-1.5, 1.5))
for px, py in reversed(right[1:])
]
return Polygon(right + left).buffer(0)
@design(aspects="any")
def draw(s: Canvas) -> None:
n, r = s.noise(21), s.rng(21)
sun_x = s.pick(landscape=(SUN_X / 1920, 0), portrait=(0.32, 0)).x
ox = sun_x - SUN_X # terrain is sun-relative, so the hill under the sun never changes
# Portrait screens stretch the ridges' depth so the land still fills the lower part.
k = max(1.0, 0.72 * s.h / 1080)
xs = np.arange(-20, s.w + 21, 8, dtype=float)
def base_y(base: float) -> float:
return s.h - k * (1080 - base)
def crest(i: int, base: float, amp: float) -> NDArray[np.float64]:
u = xs - ox
bump = 0.25 * amp * np.exp(-(((u - SUN_X) / 380) ** 2))
return base_y(base) + k * (2 * amp * n.fbm(u / 900, i * 3.1 + 0.5, 3) + bump)
def ridge(i: int, base: float, amp: float, th: float, gap: float) -> BaseGeometry:
ys = crest(i, base, amp)
shapes: list[BaseGeometry] = [
Polygon([*zip(xs, ys), (s.w + 20, s.h + 20), (-20, s.h + 20)])
]
if th:
walk: list[float] = []
x = -20.0
while x < s.w + 20:
walk.append(x)
x += gap * r.uniform(0.7, 1.2)
# Forest in loose stands: a coarse noise mask, short runs dropped so no tree stands alone.
wood = n((np.array(walk) - ox) / 240, i * 7.7) > -0.05
for a, b in runs(wood):
if b - a < 5:
continue
for x in walk[a:b]:
y = float(np.interp(x, xs, ys))
shapes.append(pine(x, y + th * 0.25, th * r.uniform(0.7, 1.2), r))
return unary_union(shapes).simplify(0.3)
hz = base_y(RIDGES[0][0])
s.fill(
P().rect(0, 0, s.w, s.h),
s.linear_gradient([(0, BG), (1, SKY)], (0, hz - 222 * k), (0, hz + 48 * k)),
)
# Sink the sun so ~45% of it sits behind the bare far hill.
base0, amp0 = RIDGES[0][0], RIDGES[0][1]
cy = float(np.interp(sun_x, xs, crest(0, base0, amp0))) - 0.08 * SUN_R
sun = (sun_x, cy)
for f, op in ((2.6, 0.05), (1.9, 0.07), (1.4, 0.09)):
s.fill(P().circle(sun, f * SUN_R), ACCENT_7, opacity=op)
s.fill(
P().circle(sun, SUN_R),
s.linear_gradient([(0, ACCENT), (1, ACCENT_2)], (0, cy - SUN_R), (0, cy + SUN_R)),
)
for i, (base, amp, th, gap, tone) in enumerate(RIDGES):
# Mist pools in the band the next ridge leaves visible, so each layer's foot lifts off the one in front.
nxt = base_y(RIDGES[i + 1][0]) if i + 1 < len(RIDGES) else s.h
fill = s.linear_gradient(
[(0, tone), (1, mix(tone, MIST, 0.35))],
(0, base_y(base) - 0.3 * amp * k),
(0, nxt),
)
s.fill(P().shape(ridge(i, base, amp, th, gap)), fill)
Run it yourself
$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render pine-mist --theme fireproof -o pine-mist-fireproof-16x9.svg