Gradients¶
Gradients let you fill or stroke any entity with smooth color transitions instead of flat colors. PyFreeform supports linear and radial gradients — the two types natively supported by SVG.
Linear Gradients¶
A linear gradient transitions colors along a straight line. Pass colors and they auto-distribute evenly:
from pyfreeform import LinearGradient
grad = LinearGradient("red", "blue")
cell.add_rect(fill=grad, width=0.9, height=0.6)
Angle¶
Control the gradient direction with angle= (degrees). 0° is left-to-right, 90° is top-to-bottom:
Multiple Colors¶
Add as many colors as you like — offsets are distributed evenly:
Custom Offsets¶
Use tuples to control where each color sits along the gradient. Offsets range from 0.0 (start) to 1.0 (end):
# Even: colors at 0.0, 0.5, 1.0
LinearGradient("red", "blue", "white")
# Custom: blue pushed to 70%, white compressed into last 30%
LinearGradient(("red", 0.0), ("blue", 0.7), ("white", 1.0))
Hard Edges¶
Repeat a color at the same offset to create an instant transition — mixing solid regions with gradients:
LinearGradient(
("black", 0.0), ("black", 0.4), # solid black: 0% → 40%
("red", 0.4), ("yellow", 1.0), # red→yellow gradient: 40% → 100%
)
Stop Opacity¶
Each color stop can also have an opacity — a third value in the tuple. SVG smoothly interpolates opacity between stops, so a gradient from fully opaque to fully transparent creates a natural fade:
LinearGradient(
("coral", 0.0, 1.0), # fully opaque at the start
("coral", 1.0, 0.0), # fully transparent at the end
)
Combine multiple stops for richer effects — opaque edges that fade to transparent in the middle:
LinearGradient(
("coral", 0.0, 1.0), # fully opaque
("#9b59b6", 0.35, 0.8), # mostly opaque
("dodgerblue", 0.5, 0.0), # fully transparent — content behind peeks through
("#9b59b6", 0.65, 0.8), # fading back in
("coral", 1.0, 1.0), # fully opaque again
)
Radial Gradients¶
A radial gradient radiates outward from a center point:
from pyfreeform import RadialGradient
grad = RadialGradient("white", "dodgerblue", "midnightblue")
cell.add_ellipse(fill=grad, rx=0.35, ry=0.35)
Focal Point¶
Shift the light source with fx= and fy= (0.0–1.0, relative to the shape):
Works Everywhere¶
Gradients work anywhere you'd use a color — fill=, color=, and stroke=:
sunset = LinearGradient("orangered", "gold", angle=90)
ocean = RadialGradient("lightskyblue", "navy")
cell.add_rect(fill=sunset, width=0.7, height=0.7)
cell.add_ellipse(fill=ocean, rx=0.35, ry=0.35)
cell.add_polygon(Polygon.hexagon(size=0.7), fill=sunset)
cell.add_dot(radius=0.35, color=ocean)
Gradient Strokes¶
Use gradients for strokes too:
cell.add_rect(
fill=None,
stroke=LinearGradient("coral", "dodgerblue"),
stroke_width=4,
width=0.85, height=0.55,
)
Gradient Reuse¶
One gradient object can fill multiple shapes — PyFreeform deduplicates automatically:
fire = LinearGradient("yellow", "orangered", "darkred", angle=90)
for i, cell in enumerate(scene.grid):
n = 3 + i # triangle through octagon
cell.add_polygon(Polygon.regular_polygon(n, size=0.7), fill=fire)
<linearGradient> in the SVG.EntityGroups¶
Gradients work inside EntityGroups too:
def make_badge(fill_grad, accent):
g = EntityGroup()
for k in range(12):
angle = k * (2 * math.pi / 12)
g.add(Dot(18 * math.cos(angle), 18 * math.sin(angle), radius=4, color=accent))
g.add(Dot(0, 0, radius=12, color=fill_grad))
return g
gold_grad = RadialGradient("lightyellow", "gold", "darkgoldenrod")
cell.add(make_badge(gold_grad, "gold"))
Creative Examples¶
Sunset Scene¶
Layer gradients to build a scene:
sky = LinearGradient(
("midnightblue", 0.0),
("darkorange", 0.6),
("gold", 0.85),
("lightyellow", 1.0),
angle=90,
)
scene.add_rect(fill=sky, width=1.0, height=1.0)
sun = RadialGradient(
("white", 0.0), ("gold", 0.3),
("orangered", 0.7), ("transparent", 1.0),
)
scene.add_ellipse(fill=sun, rx=0.12, ry=0.19, at=(0.5, 0.75))
Glowing Orbs¶
Radial gradients fading to transparent create glow effects:
glow = RadialGradient(("#ff006e", 0.0), ("transparent", 1.0))
cell.add_ellipse(fill=glow, rx=0.08, ry=0.16, at=(x, 0.5))
cell.add_dot(radius=0.02, color="#ff006e", at=(x, 0.5)) # bright core
Gem Stones¶
Combine a shifted focal point with per-stop opacity for a gem-like shine:
gem = RadialGradient(
("white", 0.0, 0.9), # (color, offset, opacity)
("#e0115f", 0.3),
("#8b0000", 1.0),
fx=0.35, fy=0.35, # off-center highlight
)
cell.add_polygon(Polygon.hexagon(size=0.75), fill=gem)
See also
For the full gradient API, see Styling & Caps — Gradients.
What's Next?¶
Add motion to your artwork with animations: