gb-vector
Pure Haskell SVG generation
https://github.com/Gondola-Bros-Entertainment/gb-vector
| Stackage Nightly 2026-03-06: | 0.1.0.5 |
| Latest on Hackage: | 0.1.0.5 |
BSD-3-Clause licensed by Devon Tomlin
Maintained by [email protected]
This version can be pinned in stack with:
gb-vector-0.1.0.5@sha256:8802ff5f39056057d2dba0af2296dfcf2174a116a43fd7286122cfde88f97fdd,1794Module documentation for 0.1.0.5
Depends on 2 packages(full list with versions):
Overview
gb-vector is a pure Haskell library for generating SVG. Define shapes, style them, compose them, and render — all with pure functions.
Companion to gb-sprite (procedural raster) and gb-synth (procedural audio).
Features:
Elementrecursive sum type — style and transforms are constructors wrapping children- Compose via function application (
translate 50 50 $ fill gold $ circle 30) or(&) - Shapes: circle, ellipse, rect, rounded rect, polygon, star, arc, ring
- Path DSL: monadic builder with lineTo, cubicTo, quadTo, arcTo, closePath
- Path operations: reverse, measure, split, offset, simplify (Ramer-Douglas-Peucker)
- Boolean operations: union, intersection, difference, XOR (Sutherland-Hodgman; clip polygon must be convex)
- Transforms: translate, rotate, rotateAround, scale, skew + affine matrix type
- Style: fill, stroke, opacity, clip, mask, blur, drop shadow
- Color: 43 named colors, hex, HSL, Oklab perceptual space, lighten/darken/saturate/invert
- Gradients: linear, radial, with stop helpers and Oklab interpolation
- Noise: Perlin, simplex, FBM, noise-driven paths, Voronoi diagrams
- Patterns: dot grid, line grid, crosshatch, checker
- SVG parsing: round-trip parse/render for basic shapes, paths, groups, transforms
- Bezier math: De Casteljau evaluation, subdivision, flattening, arc-to-cubic
- Text elements with font configuration
- Tree optimizer: collapse redundant transforms and empty groups
- Semigroup/Monoid composition on Element
- 523 tests, 92% HPC expression coverage, 100% Haddock coverage
Dependencies: base + text only. Both GHC boot libraries. Zero external deps.
Architecture
src/GBVector/
├── Types.hs V2, Segment, ArcParams, Path, enums (LineCap, LineJoin, FillRule)
├── Color.hs RGBA, rgb/rgb8/hex/hsl, 43 named colors, Oklab, lighten/darken
├── Element.hs Element tree, Fill, Gradient, StrokeConfig, FilterKind, Document
├── Path.hs PathBuilder DSL (startAt/lineTo/cubicTo/closePath/buildPath)
├── PathOps.hs reverse, measure, split, offset, simplify paths
├── Boolean.hs union, intersection, difference, XOR polygon clipping
├── Bezier.hs De Casteljau, subdivision, bbox, arc-to-cubic, flatten, length
├── Shape.hs circle, rect, roundedRect, ellipse, polygon, star, arc, ring
├── Gradient.hs linearGradient, radialGradient, stop, evenStops, oklabStops
├── Noise.hs perlin2D, simplex2D, fbm, noisePath, wobblePath, Voronoi
├── Pattern.hs dotGrid, lineGrid, crosshatch, checker, patternDef
├── Transform.hs translate, rotate, scale, skew + Matrix type with composition
├── Style.hs fill, stroke, opacity, clip, mask, blur, dropShadow, withId, use
├── Compose.hs group, empty, document, background, optimizeElement
├── Text.hs text, textAt, textWithConfig, font config builders
├── SVG.hs render :: Document -> Text, writeSvg :: FilePath -> Document -> IO ()
└── SVG/
└── Parse.hs parseSvg, parseElement — SVG text back to Element trees
Pipeline
Shapes/Paths → Style/Transform → Compose → Document → render → Text/SVG file
Usage
As a dependency
Add to your .cabal file:
build-depends: gb-vector >= 0.1
Generating SVG
import GBVector.Color (gold)
import GBVector.Compose (document)
import GBVector.SVG (writeSvg)
import GBVector.Shape (star)
import GBVector.Style (fill)
import GBVector.Transform (translate)
main :: IO ()
main = writeSvg "star.svg" $
document 200 200 $
translate 100 100 $
fill gold $
star 5 80 35
API
Color
data Color = Color !Double !Double !Double !Double -- RGBA [0,1]
rgb :: Double -> Double -> Double -> Color
rgba :: Double -> Double -> Double -> Double -> Color
rgb8 :: Int -> Int -> Int -> Color -- 0-255 channels
hex :: String -> Color -- "#ff0000", "f00", etc.
hsl :: Double -> Double -> Double -> Color -- hue (0-360), saturation, lightness
hsla :: Double -> Double -> Double -> Double -> Color
lerp :: Double -> Color -> Color -> Color -- linear interpolation
lerpOklab :: Double -> Color -> Color -> Color -- perceptual interpolation
withAlpha :: Double -> Color -> Color
toHex :: Color -> String -- "#rrggbb" or "#rrggbbaa"
lighten :: Double -> Color -> Color -- increase lightness in Oklab
darken :: Double -> Color -> Color
saturate :: Double -> Color -> Color
desaturate :: Double -> Color -> Color
invert :: Color -> Color
-- 43 named colors: black, white, red, green, blue, yellow, cyan, magenta,
-- gold, crimson, coral, navy, purple, violet, teal, olive, ...
Element
data Element
= ECircle !Double | ERect !Double !Double | EPath !Path | EGroup ![Element]
| EFill !Fill !Element | EStroke !Color !Double !Element
| ETranslate !Double !Double !Element | ERotate !Double !Element
| EScale !Double !Double !Element | EOpacity !Double !Element
| EClip !Element !Element | EFilter !FilterKind !Element
| ... -- 30 constructors total
instance Semigroup Element -- EGroup composition
instance Monoid Element -- mempty = EEmpty
Shape
circle :: Double -> Element -- radius
ellipse :: Double -> Double -> Element -- rx, ry
rect :: Double -> Double -> Element -- width, height
square :: Double -> Element
roundedRect :: Double -> Double -> Double -> Double -> Element -- w, h, rx, ry
line :: V2 -> V2 -> Element
polygon :: [V2] -> Element
regularPolygon :: Int -> Double -> Element -- sides, radius
star :: Int -> Double -> Double -> Element -- points, outer, inner
arc :: Double -> Double -> Double -> Element -- radius, start, end (radians)
ring :: Double -> Double -> Element -- outer, inner radius
Path DSL
buildPath :: PathBuilder () -> Path
startAt :: V2 -> PathBuilder ()
lineTo :: V2 -> PathBuilder ()
cubicTo :: V2 -> V2 -> V2 -> PathBuilder () -- control1, control2, end
quadTo :: V2 -> V2 -> PathBuilder () -- control, end
arcTo :: ArcParams -> V2 -> PathBuilder ()
closePath :: PathBuilder ()
polylinePath :: [V2] -> Path -- open path through points
polygonPath :: [V2] -> Path -- closed path through points
Path Operations
reversePath :: Path -> Path
measurePath :: Path -> Double -- approximate arc length
splitPathAt :: Double -> Path -> (Path, Path) -- split at t in [0,1]
subpath :: Double -> Double -> Path -> Path
offsetPath :: Double -> Path -> Path -- parallel curve approximation
simplifyPath :: Double -> Path -> Path -- Ramer-Douglas-Peucker
Boolean Operations
union :: Path -> Path -> Path
intersection :: Path -> Path -> Path
difference :: Path -> Path -> Path -- A minus B
xorPaths :: Path -> Path -> Path
pathToPolygon :: Path -> [V2]
polygonToPath :: [V2] -> Path
polygonArea :: [V2] -> Double
pointInPolygon :: V2 -> [V2] -> Bool
Transform & Style
translate :: Double -> Double -> Element -> Element
rotate :: Double -> Element -> Element -- degrees
rotateAround :: Double -> V2 -> Element -> Element
scale :: Double -> Element -> Element -- uniform
scaleXY :: Double -> Double -> Element -> Element
skewX :: Double -> Element -> Element -- degrees
skewY :: Double -> Element -> Element
fill :: Color -> Element -> Element
stroke :: Color -> Double -> Element -> Element -- color, width
opacity :: Double -> Element -> Element
fillNone :: Element -> Element
clip :: Element -> Element -> Element -- clip shape, content
mask :: Element -> Element -> Element
blur :: Double -> Element -> Element -- stdDeviation
dropShadow :: Double -> Double -> Double -> Color -> Element -> Element
Noise
perlin2D :: Int -> Double -> Double -> Double -- seed, x, y
simplex2D :: Int -> Double -> Double -> Double
fbm :: (Double -> Double -> Double) -> Int -> Double -> Double -> Double -> Double -> Double
noisePath :: (Double -> Double -> Double) -> Int -> Double -> Double -> Double -> Path
noiseClosedPath :: (Double -> Double -> Double) -> Int -> Double -> Double -> Double -> Double -> Path
wobblePath :: (Double -> Double -> Double) -> Double -> Path -> Path
jitterPoints :: (Double -> Double -> Double) -> Double -> [V2] -> [V2]
voronoiCells :: Int -> Int -> Int -> Double -> Double -> [V2]
voronoiEdges :: Int -> Int -> Int -> Double -> Double -> [(V2, V2)]
SVG Output
render :: Document -> Text -- pure serialization
renderElement :: Element -> Text -- fragment (no <svg> wrapper)
writeSvg :: FilePath -> Document -> IO ()
SVG Parsing
parseSvg :: Text -> Either ParseError Document
parseElement :: Text -> Either ParseError Element
Example
import Data.Function ((&))
import GBVector.Color (black, gold, hex, red, white)
import GBVector.Compose (background, document, group)
import GBVector.Element (Element (EPath))
import GBVector.Gradient (evenStops, linearGradient)
import GBVector.Path (buildPath, closePath, cubicTo, lineTo, startAt)
import GBVector.SVG (writeSvg)
import GBVector.Shape (circle, rect, star)
import GBVector.Style (fill, fillGradient, fillNone, stroke, withId)
import GBVector.Transform (rotate, scale, translate)
import GBVector.Types (V2 (..))
main :: IO ()
main = writeSvg "example.svg" $
document 400 400 $
background 400 400 (hex "#1a1a2e") $
group
[ -- Gold star with black outline
star 5 80 35
& fill gold
& stroke black 2
& translate 200 180
, -- Gradient circle
circle 40
& fillGradient (linearGradient (V2 0 0) (V2 80 80) (evenStops [red, gold]))
& translate 200 320
, -- Custom path
EPath (buildPath $ do
startAt (V2 50 50)
lineTo (V2 150 50)
cubicTo (V2 200 100) (V2 200 200) (V2 150 250)
lineTo (V2 50 250)
closePath)
& fillNone
& stroke white 1.5
& translate 100 50
]
Build & Test
Requires GHCup with GHC >= 9.8.
cabal build # Build library
cabal test # Run tests (523 pure tests)
cabal build --ghc-options="-Werror" # Warnings as errors
cabal haddock # Generate docs (100% coverage)
Changes
Changelog
0.1.0.5
Bug Fixes
- Fix gradient rendering under transforms. Add
gradientUnits="userSpaceOnUse"to both linear and radial gradient SVG output. Without this, browsers default toobjectBoundingBoxcoordinates which misinterpret absolute gradient positions when the element tree contains scale/translate transforms.
0.1.0.4
Improvements
- Fix all Haddock ambiguity warnings (disambiguate type-vs-constructor references)
- Fix missing link for unexported
intersectEpsilonin Boolean module docs - Add
cabal haddockstep to CI pipeline to catch doc warnings before publish
0.1.0.3
Bug Fixes
- Fix SVG parser failing to parse
width/heighton<svg>tags containing URL attributes (e.g.xmlns="http://..."). The/in quoted URLs was prematurely terminating the tag scan, causing round-triprender → parseSvgto lose document dimensions.
Improvements
- 523 tests (was 294), 92% HPC expression coverage across all 17 modules
0.1.0.2
Improvements
- Document all positional constructor arguments with
-- |comments for richer API docs on Hackage - Upgrade tested GHC from 9.6.7 to 9.8.4
- Fix GHC 9.8
-Wcompatwarnings (head/lastreplaced with pattern matching)
Affected Types
V2,ViewBox,Segment,Color— all constructor arguments documentedElement— all 30 constructors’ positional arguments documentedFill,Gradient,FilterKind— all constructor arguments documentedParseError—MalformedTagandMalformedPatharguments documented
0.1.0.1
Bug Fixes
- Fix arc segments silently replaced with straight lines in Boolean ops and PathOps
- Fix arc length measurement using straight-line distance instead of true arc length
- Fix
subpathpotential overflow whent0is very close to 1 - Fix test suite leaving temporary SVG files in project root
Improvements
cubicLengthnow uses adaptive flattening instead of uniform sampling for better accuracygrad2Dexpanded from 4 to 8 gradient directions for better noise isotropy- 100% Haddock coverage across all 17 modules (was 64%)
- Document Sutherland-Hodgman convexity requirement on
intersectionanddifference - Document
unionbehavior with non-overlapping polygons - Document
intersectEpsilonas absolute tolerance - 9 new tests (294 total): arc flattening, arc measurement, arc boolean ops, subpath edge case, saturate safety, noise determinism, arcToCubics validation
Internal
- Add
directoryto test suite dependencies for temp file cleanup
0.1.0.0
Initial release.
Core Types
V22D vector,Segment(line/cubic/quad/arc),Path(closed/open)Colortype with RGBA in[0, 1]—rgb,rgba,rgb8,hexconstructors- 43 named colors,
lerp,withAlpha,toHex hsl,hsla— HSL color construction- Oklab perceptual color space:
toOklab,fromOklab,lerpOklab - Color adjustments:
lighten,darken,saturate,desaturate,invert
Element Tree
Elementrecursive sum type — style and transforms as wrapping constructors- Compose via function application or left-to-right with
(&) Semigroup/Monoidinstance viaEGroup
Shapes
circle,rect,roundedRect,ellipse,polygon,starline,square,regularPolygon,arc,ring
Path DSL
buildPath— run aPathBuildermonad to produce aPathPathBuilderactions:startAt,lineTo,cubicTo,quadTo,arcTo,closePathpolylinePath,polygonPathconvenience constructors
Path Operations
reversePath,measurePath— reversal and arc lengthsplitPathAt,subpath— splitting and extractionoffsetPath— parallel curve approximationsimplifyPath— Ramer-Douglas-Peucker simplification
Boolean Operations
union,intersection,difference,xorPaths— polygon clippingpathToPolygon,polygonToPath— conversion utilitiespolygonArea,pointInPolygon— polygon analysis
Transforms
translate,rotate,rotateAround,scale,scaleXY,skewX,skewYMatrixtype withidentity,composeMatrix,applyMatrix- Matrix constructors:
translateM,rotateM,scaleM,scaleXYM,skewXM,skewYM
Gradients
linearGradient,radialGradient— gradient constructorsstop,stopWithOpacity,evenStops,oklabStops— gradient stop builders
Style
fill,fillColor,fillGradient,fillNone,fillRulestroke,strokeEx,dashedStroke,defaultStrokeConfigopacity,clip,mask,blur,dropShadowwithId,use,raw,title,desc
Text
text,textAt,textWithConfig— text element constructorsdefaultTextConfig,fontSize,fontFamily,bold,italic,anchor— config builders
Noise
perlin2D,simplex2D— deterministic 2D noisefbm— fractional Brownian motionnoisePath,noiseClosedPath— procedural path generationwobblePath,jitterPoints— noise-driven distortionvoronoiCells,voronoiEdges— Voronoi diagram generation
Patterns
dotGrid,lineGrid,crosshatch,checker— tileable pattern generatorspatternDef,PatternConfig,defaultPatternConfig— SVG pattern element construction
SVG Parsing
parseSvg,parseElement— parse SVG text back toElementtrees- Supports basic shapes, paths, groups, text, and presentation attributes
- Enables round-trip workflows: render, parse, manipulate, re-export
Composition
group,empty,document,documentWithViewBox,backgroundoptimizeElement— collapse redundant transforms and empty groups
SVG Output
render :: Document -> Text— pure SVG serializationrenderCompact :: Document -> Text— compact SVG outputrenderElement :: Element -> Text— render a fragment without<svg>wrapperwriteSvg :: FilePath -> Document -> IO ()— file output