fresco

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 8 Imported by: 0

README

fresco

Go Reference

Generative, free-running animated fields for the terminal. fresco paints the whole pane with a slow-drifting, theme-coloured procedural field — a nebula, a receding tunnel, a pool of ripples, a turning galaxy — as plain ANSI text. It is a pure rendering engine, not a widget: you give it a size and a frame number, it hands you a string.

frame := fresco.Render(width, height, tick, fresco.Options{
    Palette: fresco.Palette{A0: "#f7768e", A1: "#bb9af7", A2: "#7aa2f7", A3: "#7dcfff", Highlight: "#c0caf5"},
    Variant: fresco.Ripple,
})

Run go run github.com/ZviBaratz/fresco/cmd/fresco-demo@latest to see it move.

What it is (and isn't)

fresco renders open-ended, free-running fields: continuous procedural motion that never resolves, meant for splash screens, idle/empty states, and ambient backdrops. That's a different thing from the neighbours:

  • Not a spinner or progress indicator (briandowns/spinner).
  • Not a figlet/banner generator (go-figure) — it draws fields, not letters.
  • Not an image→ASCII converter (chafa, ascii-image-converter) — nothing is converted; the image is generated from math.
  • Not a preset effects roster that animates your text in and out. fresco's fields have no subject; they just drift.

Everything is deterministic and pure over its inputs — the same (width, height, frame, Options) always yields the same bytes — so it is snapshot-testable and trivial to drive from any render loop.

Install

go get github.com/ZviBaratz/fresco

Requires Go 1.25+. Depends only on the Charm colour stack (lipgloss, x/ansi) and go-colorful.

The variants

Variant What it is
fresco.Rain Matrix-style digital rain — per-column streams with bright heads and fading tails, layered at three depths. Shades by luminance alone.
fresco.Tunnel A textured wall flying past a vanishing point — screen position maps to (depth, angle), z-fog carries distance in luminance, hue bands by depth.
fresco.Ripple Drops falling on a dark pool — each flashes where it lands and expands into a hue-shifting ring; rings interfere where they cross.
fresco.Galaxy An inclined spiral turning on the focal point — soft arms, a bright core, an occluding dust lane for depth.

fresco.Variants() returns them all (the natural rotation order); fresco.ParseVariant("ripple") resolves a name.

Quickstart: an animation loop

package main

import (
	"fmt"
	"time"

	"github.com/ZviBaratz/fresco"
)

func main() {
	pal := fresco.Palette{A0: "#f7768e", A1: "#bb9af7", A2: "#7aa2f7", A3: "#7dcfff", Highlight: "#c0caf5"}
	for frame := 0; ; frame++ {
		fmt.Printf("\x1b[H%s", fresco.Render(96, 30, frame, fresco.Options{
			Palette: pal,
			Variant: fresco.Galaxy,
		}))
		time.Sleep(time.Second / 30)
	}
}

See cmd/fresco-demo for a version that cycles every variant and restores your cursor on exit.

API

Render(w, h, frame int, opts Options) string — the whole surface. It returns exactly h lines of exactly w visible cells (or "" for a degenerate pane).

type Options struct {
	Palette  Palette          // the five colour anchors (required for colour)
	Variant  Variant          // Rain, Tunnel, Ripple, or Galaxy
	FocalRow int              // the row the field emanates from; negative = centre
	LumRange *float64         // optional override for the density↔luminance split
	Profile  *termenv.Profile // optional: pin the colour depth (nil = auto-detect)
}

type Palette struct {
	A0, A1, A2, A3 string // "#rrggbb" warm→cool gradient anchors
	Highlight      string // the star / rain-head near-white
}

Colour depth. By default fresco auto-detects the terminal's colour profile (truecolor / 256 / 16 / none). Set Options.Profile to pin it — useful for tests, for writing to a non-TTY, or for forcing colour when piping. With it set, Render is pure over its inputs regardless of the ambient terminal.

Origin

fresco was extracted from Atrium, where it is the animated empty-state splash. The engine is original work; the app-side scene composition (a logo overlay, variant selection) stayed behind in Atrium.

License

MIT © Zvi Baratz

Documentation

Overview

Package fresco is a generative terminal field engine: it renders a slow-drifting, palette-coloured pattern that appears to emanate from a focal row. It is a self-contained engine — the field math, the gradient LUT, and the variant vocabulary — that the caller drives through Render. The caller owns any scene composition (an overlay/message on top) and variant selection.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Render

func Render(w, h, frame int, opts Options) string

Render builds the colored splash field background: exactly h rows of exactly w visible cells, or "" on a degenerate pane. Pure over its inputs (deterministic, snapshot-testable). It resolves the focal row and the per-variant Pass-2 policy (applying Options.LumRange over the variant default) and hands off to renderField.

Types

type Options

type Options struct {
	// Palette supplies the four warm→cool gradient anchors plus the star /
	// rain-head highlight.
	Palette Palette
	// Variant selects the field generator.
	Variant Variant
	// FocalRow is the pane row the pattern emanates from — the origin of the
	// focal-relative coordinates every field is evaluated in. A negative value
	// centres it on the pane.
	FocalRow int
	// LumRange, when non-nil, overrides the variant's shipped luminance-range
	// policy (the split between glyph density and colour luminance); nil keeps
	// the variant's default. It is how a caller applies a luminance-range
	// override without this package reading the environment.
	LumRange *float64
	// Profile, when non-nil, pins the color depth Render emits at (truecolor,
	// 256, 16, or none), independent of the process-global color profile. nil
	// defers to lipgloss.ColorProfile() — the auto-detected terminal profile —
	// which is what a caller wants when rendering to a real pane. Setting it
	// makes Render pure over its inputs: the same Options yield the same bytes
	// regardless of ambient stdout state, which is what a standalone consumer
	// (and a snapshot test) needs.
	Profile *termenv.Profile
}

Options configures Render.

type Palette

type Palette struct {
	A0, A1, A2, A3 string
	Highlight      string
}

Palette is the splash field's colour input: four warm→cool gradient anchors plus a highlight, each an "#rrggbb" hex string. A0..A3 are the nebula gradient (e.g. pink, purple, blue, cyan); consecutive anchors are meant to be hue-adjacent so HCL blending between them stays smooth. A3 doubles as rain's stream hue. Highlight is the star / rain-head white — the brightest colour the field can reach (the foreground / near-white). An anchor that is not parseable hex degrades gracefully (see splashGradientColors, splashShadeParse).

type Variant

type Variant int

Variant selects the field generator + glyph technique. The caller pins one (or keeps a random per-launch rotation) and passes it in through Options; a caller-side override affordance can trump that.

const (
	// Rain ("f") is Matrix-style digital rain: per-column streams with bright
	// heads and fading tails, layered at three depths. The roster's motion entry
	// — and the only variant that shades by luminance alone, with no hue of its
	// own to spend (see buildRainRamp).
	//
	// It is also the fallback: a variant with no case in splashFieldAt or ops
	// renders as rain, and a caller resolves an unrecognized override name to
	// it too.
	Rain Variant = iota
	// Tunnel ("g") is a textured wall flying past a vanishing point that sits on
	// the focal point: screen position maps to (depth, angle), so a plain noise
	// lookup becomes an infinite corridor. The roster's depth entry — z-fog
	// carries distance in luminance, and hue bands by depth into coloured rings
	// receding down the wall.
	Tunnel
	// Ripple ("h") is drops falling on a dark pool: each one flashes where it
	// lands and expands into a ring that shifts hue as it ages, and the rings
	// interfere where they cross. The roster's event entry — the only field with
	// a birth and a death in it rather than a steady state.
	Ripple
	// Galaxy ("i") is an inclined spiral turning around the focal point: a soft
	// bright bulge, arms mottled with turbulence and star-knots, a dust lane
	// silhouetting the disk's near edge, warm at the core and cool at the rim. The
	// tunnel's single-object sibling — brightness is the whole subject, and the
	// arms are a rigidly rotating density wave rather than winding matter (see
	// splashGalaxyAtFor).
	Galaxy
)

func ParseVariant

func ParseVariant(s string) (Variant, bool)

ParseVariant resolves a pinnable pattern name to its variant. It knows only the user-facing names, not the dev letters (f/g/h) — those are a caller-side affordance layered on top. The bool is false for an unknown name.

func Variants

func Variants() []Variant

Variants lists the shipped variants — the pool a caller's random rotation draws from. The order is the rotation order; the returned slice is a fresh copy, so callers cannot mutate the pool.

func (Variant) String

func (v Variant) String() string

String returns the variant's pinnable pattern name, or "unknown" for a value outside the shipped set.

Directories

Path Synopsis
cmd
fresco-demo command
Command fresco-demo animates the fresco fields in your terminal.
Command fresco-demo animates the fresco fields in your terminal.

Jump to

Keyboard shortcuts

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