Skip to content

Colors, Styles & Palettes

PyFreeform provides a coherent color and styling system. Master three concepts: the color parameter split, style objects, and palettes.

The Color Parameter Split

This is the most important API distinction to remember:

Parameter Used by Description
color= Dot, Line, Curve, Text, add_fill, add_border Stroke-like entities
fill= Rect, Ellipse, Polygon Filled shapes

fill= vs color= comparison

Top row: entities using color=. Bottom row: entities using fill=.

Common mistake

Writing cell.add_polygon(verts, color="red") will raise an error. Use fill="red" for shapes.

Color Formats

All color parameters accept any ColorLike value:

  • Named colors: "red", "coral", "navy", "salmon"
  • Hex strings: "#ff6b6b", "#f00", "#FF6B6B"
  • RGB tuples: (255, 107, 107), cell.rgb

Opacity

Every entity supports opacity (0.0 transparent → 1.0 opaque, default 1.0):

cell.add_dot(radius=0.1, color="coral", opacity=0.5)
cell.add_fill(color="navy", opacity=0.3)

Shape-Specific: fill_opacity & stroke_opacity

Shapes (Rect, Ellipse, Polygon) support independent opacity for fill and stroke:

cell.add_ellipse(
    rx=0.45, ry=0.45,
    fill=colors.primary,
    stroke=colors.accent,
    stroke_width=3,
    fill_opacity=0.4,     # Semi-transparent fill
    stroke_opacity=1.0,   # Fully opaque stroke
)

Fill opacity progression

Fill opacity from 0.2 to 1.0 with constant stroke opacity.

Layered Opacity

Stack semi-transparent shapes for color mixing effects:

Opacity layering

Three overlapping circles at 50% opacity — colors blend where they overlap.

Brightness

Scale any color toward black with a brightness multiplier (0.0 = black, 1.0 = unchanged).

color_brightness

For color= entities (Dot, Line, Curve, Text, Fill, Border, Connection):

# Map image brightness directly to color intensity
for cell in scene.grid:
    cell.add_dot(color="white", color_brightness=cell.brightness)

Brightness-driven dots

White dots dimmed by brightness — dark cells produce darker dots.

fill_brightness & stroke_brightness

For fill=/stroke= shapes (Rect, Ellipse, Polygon), mirroring fill_opacity/stroke_opacity:

cell.add_ellipse(
    fill="coral", stroke="navy",
    fill_brightness=cell.brightness,   # Dim the fill
    stroke_brightness=1.0,             # Keep stroke at full intensity
)

Fill brightness progression

Fill brightness from 0.2 to 1.0 with constant stroke brightness.

Utility Functions

Two standalone functions for working with brightness outside builders:

from pyfreeform.color import apply_brightness, gray

apply_brightness("coral", 0.5)  # Half-bright coral → "#7f3f28"
apply_brightness("white", 0.0)  # Pure black → "#000000"
gray(0.5)                       # Mid-gray → "#808080"
gray(1.0)                       # White → "#ffffff"

gray(b) is shorthand for apply_brightness("white", b).


Style Objects

Instead of repeating parameters, define a style object once and reuse it:

from pyfreeform import FillStyle, PathStyle, ShapeStyle

dot_small = FillStyle(color="coral", opacity=0.6)
dot_large = FillStyle(color="gold", opacity=0.9)
line_thin = PathStyle(width=1, color="#666688", opacity=0.4)
shape_hex = ShapeStyle(color="teal", opacity=0.5)

for cell in scene.grid:
    cell.add_dot(radius=0.15, style=dot_small)  # Apply directly
    cell.add_line(start="top", end="bottom", style=line_thin)

Style reuse

Three zones using different pre-defined styles — consistent look with no parameter repetition.

All Style Classes

Class For Methods Key Fields
FillStyle add_dot(), add_fill() color, opacity, color_brightness
PathStyle add_line(), add_diagonal(), add_curve(), add_path(), Connection width, color, cap, start_cap, end_cap, color_brightness
BorderStyle add_border() width, color, opacity, color_brightness
ShapeStyle add_ellipse(), add_polygon(), add_rect() color (→ fill), stroke, stroke_width, fill_brightness, stroke_brightness
TextStyle add_text() color, font_family, bold, italic, color_brightness

Styles are dataclasses. Use dataclasses.replace() to create modified copies:

from dataclasses import replace

base = PathStyle(width=2, color="coral")
thick = replace(base, width=4)                # New style, width=4
arrow = replace(base, end_cap="arrow")        # New style, with arrow cap

Palettes

8 pre-built color palettes with 6 named colors each:

Midnight

Midnight

Sunset

Sunset

Ocean

Ocean

Forest

Forest

Monochrome

Monochrome

Paper

Paper

Neon

Neon

Pastel

Pastel

Using Palettes

from pyfreeform import Palette

colors = Palette.midnight()
scene = Scene.with_grid(cols=10, rows=10, cell_size=20, background=colors.background)

for cell in scene.grid:
    cell.add_dot(color=colors.primary)
    cell.add_border(color=colors.grid, width=0.3)

Named Colors

Name Purpose
colors.background Scene background
colors.primary Main element color
colors.secondary Supporting element color
colors.accent Highlight/emphasis color
colors.line Lines and connections
colors.grid Grid borders

Custom Palettes

my_palette = Palette(
    background="#1a1a2e",
    primary="#ff6b6b",
    secondary="#4ecdc4",
    accent="#ffe66d",
    line="#666688",
    grid="#3d3d5c",
)

Modify palettes with dataclasses.replace(): replace(colors, background="#000").


See also

For the full styling API, see Styling & Caps.

What's Next?

Learn the "killer feature" — positioning entities along any path:

← Drawing with Entities Paths & Parametric Positioning →