sketchy

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2026 License: MIT Imports: 30 Imported by: 5

README

sketchy_logo

Sketchy is a framework for making generative art in Go. It is inspired by vsketch and openFrameworks. It uses canvas for drawing and the ebiten game engine for the GUI.

Sketches are code-first: you construct a sketchy.Config, call sketchy.New, assign BuildUI to register controls with UI helpers (FloatSlider, IntSlider, Checkbox, ColorPicker, Dropdown, Folder, etc.), then implement Updater and Drawer. Control values are read with GetFloat / GetInt / Toggle using folder and name (use "" for the root folder). There is no sketch.json for controls or layout.

The Getting Started guide walks through install, sketchy init, and a small “Hello Circle” sketch using the code-first API; the examples/ directory has full programs you can copy from.

Below are a couple of screenshots from the example sketches:

Fractal

fractal_example

Noise

noise_example

10PRINT

10print_example

Installation

Sketchy tracks a recent Go toolchain (see go.mod for the exact minimum). Install the sketchy CLI with:

go install github.com/aldernero/sketchy/cmd/sketchy@latest

Ensure $(go env GOPATH)/bin (or your GOBIN directory) is on your PATH so the sketchy command is found.

Running the examples

From any directory, run a tagged example package with go run (no separate clone required):

go run github.com/aldernero/sketchy/examples/lissajous@latest

Swap lissajous for another folder name under examples/.

Creating a new sketch

The CLI syntax is sketchy init project_name. That creates a new directory, copies the embedded template (main.go, .gitignore), runs go mod init and go mod tidy:

❯ sketchy init mysketch
❯ tree mysketch
mysketch
├── go.mod
├── go.sum
├── main.go
└── .gitignore

Edit main.go: set fields on sketchy.Config (title, size, colors, DefaultBackground / DefaultForeground / DefaultStrokeWidth, …), register controls in BuildUI, then call Init. The template loads icon.png if present for the window icon.

Running a sketch

sketchy run project_name changes into that directory and runs go run . (expects a main.go).

The control panel

The control panel is built with debugui, an Ebitengine-oriented UI toolkit; see that repository for API details and licensing.

control_panel

User-defined controls

  • Foldersui.Folder("Title", func() { … }) groups controls under a collapsible header.
  • Float sliders — Track plus a text field for the value (similar to lil-gui). Values are validated as floats; scientific notation such as 1e-12 is accepted. Use FloatSliderDecimals when you want a fixed number of digits after the decimal in the text box; plain FloatSlider derives display precision from the step. Secondary-click (e.g. right-click) on the slider or value opens a range/step editor modal.
  • Int sliders — Same pattern with integer-only text validation and stepping.
  • Checkboxes, buttons, color pickers, dropdowns — See ui_builder.go.

Builtins

The Builtins header is fixed by Sketchy (not part of your uiPlan):

  • Seed — Integer seed and Rand button; mirrors RandomSeed.
  • Default background / Default foreground — Color pickers; define the canvas clear color and the initial stroke color before your Drawer runs. The margin around the letterboxed sketch uses a dark grey (Dark theme) or light grey (Light theme) so the drawable area reads clearly against the window border.
  • Default stroke width — Millimeters, text field with clamped range.
  • Save Image… / Take Snapshot… / Load Snapshot… — Dialogs for PNG/SVG export and SQLite-backed snapshots (see below).

The panel is hidden from rasterized sketch output. Close or reopen it with Ctrl+Space (plain Space is reserved for typing in text fields).

Saving images and snapshots

  • Save Image… — Writes under saves/png/ and/or saves/svg/ relative to the process working directory (usually your sketch project). Saves can be recorded in sketch.db.
  • Snapshots — Stored in sketch.db with:
    • control_json — Sliders, int sliders, toggles, user color pickers, dropdowns.
    • builtin_json — Default background/foreground (hex), default stroke width (mm), and random seed so builtins round-trip with the rest of the controls.

First run creates or migrates the database.

There are no default single-key bindings for “save PNG/SVG/config JSON” in the current codebase; use the Builtins dialogs (and Esc for Ebitengine’s screenshot key if you set EBITEN_SCREENSHOT_KEY in Init).

Keyboard shortcuts (control panel / seed)

Key Action
/ Increment / decrement random seed
/ Randomize seed
Ctrl+Space Show / hide control panel

Window and viewport

The sketch view can be resized; content is letterboxed/panned when the window aspect differs from the sketch. WindowSize reflects the outer window size used by Ebitengine.

Documentation

Index

Constants

View Source
const (
	DefaultControlWindowWidth  = 330
	DefaultControlWindowHeight = 500
	DefaultControlWindowX      = 25
	DefaultControlWindowY      = 25
	DefaultSliderTextWidth     = 100
	// ControlLabelColumnWidth: name column for sliders, colors, dropdowns (~20 chars at 6px/glyph + debugui padding).
	ControlLabelColumnWidth = 136
	DefaultCheckboxColumns  = 1
	DefaultButtonColumns    = 1
)
View Source
const (
	DefaultTitle  = "Sketch"
	DefaultPrefix = "sketch"
	MmPerPx       = 0.26458333
	DefaultDPI    = 96.0
	// Performance tuning constants
	DefaultPreviewDPI = 48.0 // Lower DPI for preview mode
	SaveChannelBuffer = 10   // Buffer size for async save requests

	// Default ebiten window size is sketch dimensions, clamped to at least this.
	MinWindowWidth  = 640
	MinWindowHeight = 480
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ColorPicker added in v0.2.0

type ColorPicker struct {
	Folder        string
	Name          string
	Color         string
	DidJustChange bool
	// contains filtered or unexported fields
}

func NewColorPicker added in v0.2.0

func NewColorPicker(name, color string) ColorPicker

func (*ColorPicker) GetColor added in v0.2.0

func (c *ColorPicker) GetColor() color.Color

func (*ColorPicker) GetHex added in v0.2.0

func (c *ColorPicker) GetHex() string

func (*ColorPicker) UpdateState added in v0.2.0

func (c *ColorPicker) UpdateState()

type Config added in v0.2.0

type Config struct {
	Title                  string
	Prefix                 string
	SketchWidth            float64
	SketchHeight           float64
	ControlWidth           int
	ControlHeight          int
	ControlBackgroundColor string
	ControlOutlineColor    string
	// SketchBackgroundColor is currently unused at runtime (letterbox uses Builtins dark/light theme).
	SketchBackgroundColor     string
	SketchOutlineColor        string
	DisableClearBetweenFrames bool
	// DisableFastStroke leaves canvas path settling enabled (slower, more precise strokes).
	// When false (default), Init sets canvas.FastStroke for generative-art performance.
	DisableFastStroke bool
	ShowFPS           bool
	RasterDPI         float64
	PreviewMode       bool
	RandomSeed        int64
	// DefaultBackground is the canvas clear color before Drawer runs; nil means black at Init.
	DefaultBackground color.Color
	// DefaultForeground is the initial stroke (and default pen) color for the canvas context; nil means white at Init.
	DefaultForeground color.Color
	// DefaultStrokeWidth is the initial stroke width in millimeters; 0 means 0.5 at Init.
	DefaultStrokeWidth float64
	// Images lists files to load at Init; use Image/DrawNamedImage in Drawer by Name.
	Images []ImageAsset
}

Config holds sketch options set from code (no JSON).

type Dropdown struct {
	Folder        string
	Name          string
	Options       []string
	Index         int
	DidJustChange bool
	// contains filtered or unexported fields
}

Dropdown is a string option list control.

func (d *Dropdown) Selected() string
func (d *Dropdown) UpdateState()

type FloatSlider added in v0.2.0

type FloatSlider struct {
	Folder        string
	Name          string
	MinVal        float64
	MaxVal        float64
	Val           float64
	Incr          float64
	DidJustChange bool

	// TextDecimals is fraction digits for the value text when >= 0; when < 0, use digits derived from Incr.
	TextDecimals int
	// contains filtered or unexported fields
}

FloatSlider is a continuous control backed by debugui SliderF and a text field for the value.

func NewFloatSlider added in v0.2.0

func NewFloatSlider(name string, min, max, val, incr float64) FloatSlider

func NewFloatSliderWithDecimals added in v0.2.0

func NewFloatSliderWithDecimals(name string, min, max, val, incr float64, textDecimals int) FloatSlider

NewFloatSliderWithDecimals is like NewFloatSlider but sets TextDecimals for the value text field. Pass textDecimals < 0 to derive fraction digits from incr (same as SliderF precision).

func (*FloatSlider) CalcDigits added in v0.2.0

func (s *FloatSlider) CalcDigits()

func (*FloatSlider) GetPercentage added in v0.2.0

func (s *FloatSlider) GetPercentage() float64

func (*FloatSlider) Randomize added in v0.2.0

func (s *FloatSlider) Randomize()

func (*FloatSlider) StringVal added in v0.2.0

func (s *FloatSlider) StringVal() string

func (*FloatSlider) UpdateState added in v0.2.0

func (s *FloatSlider) UpdateState()

type ImageAsset added in v0.3.0

type ImageAsset struct {
	Name string
	Path string
}

ImageAsset names an image file to load at Init. Path is relative to the sketch working directory unless absolute. Name is the key used with Image, DrawNamedImage, and DrawNamedImageAt.

type IntSlider added in v0.2.0

type IntSlider struct {
	Folder        string
	Name          string
	MinVal        int
	MaxVal        int
	Val           int
	Incr          int
	DidJustChange bool
	// contains filtered or unexported fields
}

IntSlider is a discrete stepped control backed by debugui Slider and a text field for the value.

func NewIntSlider added in v0.2.0

func NewIntSlider(name string, min, max, val, incr int) IntSlider

func (*IntSlider) Randomize added in v0.2.0

func (s *IntSlider) Randomize()

func (*IntSlider) UpdateState added in v0.2.0

func (s *IntSlider) UpdateState()

type SaveRequest added in v0.2.0

type SaveRequest struct {
	RelPath  string // e.g. saves/png/foo.png
	Format   string // "png" or "svg"
	DPI      float64
	RecordDB bool
}

SaveRequest is an async save operation (relative path under workDir).

type Sketch

type Sketch struct {
	Title                  string
	Prefix                 string
	SketchWidth            float64
	SketchHeight           float64
	ControlWidth           int
	ControlHeight          int
	ControlBackgroundColor string
	ControlOutlineColor    string
	// SketchBackgroundColor is unused for window filling; letterbox margins follow the Builtins UI theme.
	// Kept for API compatibility; may be used again if margins become configurable.
	SketchBackgroundColor string
	SketchOutlineColor    string
	// DefaultBackground is the canvas clear color before each draw (image/color, default black).
	DefaultBackground color.Color
	// DefaultForeground is the initial stroke color for the canvas context before Drawer (default white).
	DefaultForeground color.Color
	// DefaultStrokeWidth is the initial stroke width in millimeters (default 0.5).
	DefaultStrokeWidth        float64
	DisableClearBetweenFrames bool
	DisableFastStroke         bool
	ShowFPS                   bool
	RasterDPI                 float64
	PreviewMode               bool
	RandomSeed                int64

	FloatSliders []FloatSlider
	IntSliders   []IntSlider
	Toggles      []Toggle
	ColorPickers []ColorPicker
	Dropdowns    []Dropdown

	// BuildUI registers controls; set before Init().
	BuildUI func(s *Sketch, ui *UI)

	Updater               SketchUpdater
	Drawer                SketchDrawer
	DidControlsChange     bool
	DidSlidersChange      bool
	DidTogglesChange      bool
	DidColorPickersChange bool
	DidDropdownsChange    bool
	Rand                  gaul.Rng

	Tick         int64
	SketchCanvas *canvas.Canvas
	// contains filtered or unexported fields
}

func New added in v0.2.0

func New(cfg Config) *Sketch

New returns an uninitialized sketch. Set BuildUI, Updater, and Drawer, then call Init().

func (*Sketch) CanvasCoords

func (s *Sketch) CanvasCoords(x, y float64) gaul.Point

func (*Sketch) CanvasRect

func (s *Sketch) CanvasRect() gaul.Rect

func (*Sketch) Clear

func (s *Sketch) Clear()

func (*Sketch) ColorPicker added in v0.2.0

func (s *Sketch) ColorPicker(name string) color.Color

ColorPicker returns root-folder color.

func (*Sketch) ControlPanelScreenRect added in v0.2.0

func (s *Sketch) ControlPanelScreenRect() image.Rectangle

ControlPanelScreenRect returns the screen-space bounds of the control panel window (must match [Sketch.controlWindow]).

func (*Sketch) Draw

func (s *Sketch) Draw(screen *ebiten.Image)

func (*Sketch) DrawImage added in v0.3.0

func (s *Sketch) DrawImage(c *canvas.Context, img image.Image)

DrawImage draws img at (x, y) in canvas coordinates (millimeters), mapping one image pixel to one sketch pixel using the sketch's pixel-to-mm scale.

func (*Sketch) DrawImageAt added in v0.3.0

func (s *Sketch) DrawImageAt(c *canvas.Context, x, y float64, img image.Image)

DrawImageAt is like DrawImage but offsets the top-left of img to (x, y).

func (*Sketch) DrawNamedImage added in v0.3.0

func (s *Sketch) DrawNamedImage(c *canvas.Context, name string)

DrawNamedImage draws a Config ImageAsset (or RegisterImage entry) at the origin.

func (*Sketch) DrawNamedImageAt added in v0.3.0

func (s *Sketch) DrawNamedImageAt(c *canvas.Context, x, y float64, name string)

DrawNamedImageAt draws a named asset at (x, y).

func (*Sketch) Dropdown added in v0.2.0

func (s *Sketch) Dropdown(name string) int

Dropdown is shorthand for GetDropdownIndex("", name).

func (*Sketch) EnqueueSave added in v0.2.0

func (s *Sketch) EnqueueSave(relPath, format string, dpi float64, recordDB bool)

func (*Sketch) GetBool added in v0.2.0

func (s *Sketch) GetBool(folder, name string) bool

GetBool returns checkbox state (or button pulse state).

func (*Sketch) GetColor added in v0.2.0

func (s *Sketch) GetColor(folder, name string) color.Color

GetColor returns a color picker value.

func (*Sketch) GetDropdownIndex added in v0.2.0

func (s *Sketch) GetDropdownIndex(folder, name string) int

GetDropdownIndex returns selected index for a dropdown.

func (*Sketch) GetFloat added in v0.2.0

func (s *Sketch) GetFloat(folder, name string) float64

GetFloat returns a float slider value in folder (use "" for root).

func (*Sketch) GetInt added in v0.2.0

func (s *Sketch) GetInt(folder, name string) int

GetInt returns an int slider value in folder (use "" for root).

func (*Sketch) Height

func (s *Sketch) Height() float64

func (*Sketch) Image added in v0.3.0

func (s *Sketch) Image(name string) image.Image

Image returns a configured or runtime-registered image by name.

func (*Sketch) Init

func (s *Sketch) Init()

func (*Sketch) Int added in v0.2.0

func (s *Sketch) Int(name string) int

Int returns an int slider in the root folder. Prefer GetInt("", name).

func (*Sketch) IsMouseOverControlPanel added in v0.2.0

func (s *Sketch) IsMouseOverControlPanel() bool

IsMouseOverControlPanel reports whether the cursor is over the control panel's default screen rectangle or any debug UI hover surface (dialogs, dropdowns, a dragged panel).

func (*Sketch) Layout

func (s *Sketch) Layout(outsideWidth, outsideHeight int) (int, int)

func (*Sketch) MarkDirty added in v0.2.0

func (s *Sketch) MarkDirty()

MarkDirty schedules a full sketch raster pass on the next Draw. Sketchy also sets dirty when a primary pointer press lands in the sketch (same rules as Sketch.PrimaryPointerPressInSketch). Call MarkDirty when the picture changes without such a press (keyboard, timers, other mouse buttons, or state updates that bypass control-driven invalidation).

func (*Sketch) PointInSketchArea

func (s *Sketch) PointInSketchArea(x, y float64) bool

func (*Sketch) PrimaryPointerPressInSketch added in v0.2.0

func (s *Sketch) PrimaryPointerPressInSketch() (ok bool, wx, wy float64)

PrimaryPointerPressInSketch reports whether the left mouse button or a newly pressed touch just went down over the sketch (not on the control panel's default rectangle). It returns the window coordinates of that press. For the mouse, Sketch uses an edge detector ([Sketch.refreshPrimaryMouseEdge]) instead of inpututil.IsMouseButtonJustPressed, because the latter only holds when the press timestamp matches the current UI tick, which can fail depending on platform and frame timing. For touch, use the returned coordinates — not ebiten.CursorPosition, which is unset on many mobile builds. If this is true for the current frame, Sketch.Update marks the sketch dirty after Sketch.Updater returns, so the next Sketch.Draw re-rasterizes without Sketch.MarkDirty.

func (*Sketch) PrimaryPressInSketch added in v0.2.0

func (s *Sketch) PrimaryPressInSketch() bool

PrimaryPressInSketch is like Sketch.PrimaryPointerPressInSketch but only reports whether a qualifying press occurred.

func (*Sketch) RandomHeight

func (s *Sketch) RandomHeight() float64

func (*Sketch) RandomWidth

func (s *Sketch) RandomWidth() float64

func (*Sketch) RandomizeSlider

func (s *Sketch) RandomizeSlider(name string)

func (*Sketch) RandomizeSliderIn added in v0.2.0

func (s *Sketch) RandomizeSliderIn(folder, name string)

func (*Sketch) RandomizeSliders

func (s *Sketch) RandomizeSliders()

func (*Sketch) RegisterImage added in v0.3.0

func (s *Sketch) RegisterImage(name string, img image.Image)

RegisterImage adds or replaces a named image after Init (e.g. for images created in code).

func (*Sketch) SelectedDropdown added in v0.2.0

func (s *Sketch) SelectedDropdown(name string) string

SelectedDropdown returns the selected string for a root-folder dropdown.

func (*Sketch) SetBool added in v0.2.0

func (s *Sketch) SetBool(folder, name string, v bool)

SetBool sets checkbox state.

func (*Sketch) SetFloat added in v0.2.0

func (s *Sketch) SetFloat(folder, name string, v float64)

SetFloat sets a float slider value.

func (*Sketch) SetInt added in v0.2.0

func (s *Sketch) SetInt(folder, name string, v int)

SetInt sets an int slider value (clamped to min/max on next UI sync; immediate assign here).

func (*Sketch) SketchCoords

func (s *Sketch) SketchCoords(sx, sy float64) gaul.Point

SketchCoords maps sketch bitmap pixel coordinates (origin top-left of the raster) to canvas mm.

func (*Sketch) Slider

func (s *Sketch) Slider(name string) float64

Slider returns a float slider in the root folder. Prefer GetFloat("", name).

func (*Sketch) Toggle

func (s *Sketch) Toggle(name string) bool

Toggle returns root-folder checkbox/button state.

func (*Sketch) Update

func (s *Sketch) Update() error

func (*Sketch) UpdateControls

func (s *Sketch) UpdateControls()

func (*Sketch) Width

func (s *Sketch) Width() float64

func (*Sketch) WindowSize added in v0.2.0

func (s *Sketch) WindowSize() (w, h int)

WindowSize returns outer window dimensions for ebiten: the sketch size in pixels, with width and height each at least MinWindowWidth and MinWindowHeight.

func (*Sketch) WindowToSketchPixels added in v0.2.0

func (s *Sketch) WindowToSketchPixels(wx, wy float64) (sx, sy float64)

WindowToSketchPixels maps game-surface coordinates into the sketch bitmap drawn at Translate(viewPad - scroll), i.e. the inverse of the Draw path for s.offscreen.

type SketchDrawer

type SketchDrawer func(s *Sketch, ctx *canvas.Context)

type SketchUpdater

type SketchUpdater func(s *Sketch)

type Toggle

type Toggle struct {
	Folder        string
	Name          string
	Pos           gaul.Point
	Width         float64
	Height        float64
	Checked       bool
	IsButton      bool
	DidJustChange bool
	// contains filtered or unexported fields
}

func (*Toggle) UpdateState

func (t *Toggle) UpdateState()

type UI added in v0.2.0

type UI struct {
	// contains filtered or unexported fields
}

UI registers controls inside BuildUI. Call Folder() to group controls under a collapsible header.

func (*UI) Button added in v0.2.0

func (u *UI) Button(name string)

Button adds a momentary button (exposes Checked toggling on click, same as before).

func (*UI) Checkbox added in v0.2.0

func (u *UI) Checkbox(name string, checked bool)

Checkbox adds a checkbox in the current folder.

func (*UI) ColorPicker added in v0.2.0

func (u *UI) ColorPicker(name, initial string)

ColorPicker adds a color control in the current folder (initial color as hex or HTML color name).

func (*UI) Dropdown added in v0.2.0

func (u *UI) Dropdown(name string, options []string, selectedIndex int)

Dropdown adds a dropdown in the current folder. options must be non-empty.

func (*UI) FloatSlider added in v0.2.0

func (u *UI) FloatSlider(name string, min, max, val, incr float64)

FloatSlider adds a float slider and value text field in the current folder.

func (*UI) FloatSliderDecimals added in v0.2.0

func (u *UI) FloatSliderDecimals(name string, min, max, val, incr float64, decimals int)

FloatSliderDecimals is like FloatSlider but sets the number of fraction digits shown in the value text when decimals >= 0. Use decimals < 0 to derive fraction digits from incr (same precision as the slider thumb).

func (*UI) Folder added in v0.2.0

func (u *UI) Folder(title string, fn func())

Folder runs fn with the given header title as the current folder. Use "" for controls at the root (no extra header).

func (*UI) IntSlider added in v0.2.0

func (u *UI) IntSlider(name string, min, max, val, incr int)

IntSlider adds an integer stepped slider and value text field in the current folder.

Directories

Path Synopsis
cmd
sketchy command
examples
10print command
bezier command
fractal command
lissajous command
noise command
photo_stripes command
scale_rotate command
simple command
voronoi command
internal
visual_tests
color_gradient command
curve_lerp command
kdtree_mouse command
perpendicular command
quadtree_mouse command
random_points command
vector_normals command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL