Help us shape the future of Go! Take the 2026 Go Developer Survey

playground

package module
v0.0.0-...-b4486ca Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: BSD-3-Clause Imports: 14 Imported by: 0

Documentation

Overview

Package playground is the go-fft WebAssembly playground: a go-widgets canvas application in which a visitor builds a signal, picks a transform and sees what github.com/go-fft/fft makes of it — computed by go-fft itself, compiled to WebAssembly, with no JavaScript FFT anywhere.

The package is tagless and native-testable. dsp.go is the model (signals, transforms, the round trip and the timing), codegen.go writes the Go and the numpy equivalent of a configuration, viewmodel.go holds every piece of UI state as go-widgets/mvvm observables, and scene.go is the View: toolkit widgets bound to the view-model. cmd/playground-wasm is the thin js/wasm shell.

Index

Constants

View Source
const MaxListed = 4096

MaxListed is the longest signal whose samples the editor lists. A million numbers in a text box helps nobody and costs a second to lay out.

View Source
const MaxN = 1 << 20

MaxN is the longest signal the playground accepts: long enough to show that a big transform is fast, short enough that a careless paste cannot hang a tab.

Variables

View Source
var (
	LightPalette = Palette{
		Background: toolkit.RGB(0xff, 0xff, 0xff), Surface: toolkit.RGB(0xf7, 0xf8, 0xfb),
		Ink: toolkit.RGB(0x0f, 0x11, 0x15), Line: toolkit.RGB(0xe7, 0xe9, 0xee),
		Border: toolkit.RGB(0xcf, 0xd4, 0xdd), Accent: toolkit.RGB(0x04, 0x78, 0x57),
	}
	DarkPalette = Palette{
		Background: toolkit.RGB(0x0b, 0x0e, 0x14), Surface: toolkit.RGB(0x14, 0x1a, 0x24),
		Ink: toolkit.RGB(0xe6, 0xed, 0xf3), Line: toolkit.RGB(0x23, 0x2a, 0x36),
		Border: toolkit.RGB(0x3a, 0x45, 0x56), Accent: toolkit.RGB(0x34, 0xd3, 0x99),
	}
)

LightPalette and DarkPalette are the landing's own tokens (--bg, --bg-soft, --ink, --line, --elev-border and --accent from the shared landing stylesheet, with go-fft's [params.brand] accent). They are the fallback for a host that passes none, and what the native tests draw with.

View Source
var NormNames = []string{"backward", "ortho", "forward"}

NormNames are numpy's names for the three fft.Norm values, in Norm order.

View Source
var PrecisionNames = []string{"float64 (complex128)", "float32 (complex64)"}

PrecisionNames are the precisions in Precision order.

View Source
var PresetNames = []string{"Sum of two sines", "Chirp f1 → f2", "Square wave", "Noise + tone", "Impulse", "Custom samples"}

PresetNames are the presets in Preset order, as the picker lists them.

View Source
var TransformNames = []string{"FFT", "IFFT", "RFFT", "IRFFT", "DCT", "IDCT", "DST", "IDST"}

TransformNames are the transforms in Transform order, as the picker lists them.

View Source
var TypeNames = []string{"Type I", "Type II", "Type III", "Type IV"}

TypeNames are the DCT/DST types as the picker lists them (index 0 is type I).

View Source
var ViewNames = []string{"Magnitude |y|", "Magnitude (dB)", "Phase (rad)", "Real and imaginary"}

ViewNames are the views in View order.

View Source
var WindowNames = []string{"None (rectangular)", "Hann", "Hamming", "Blackman", "Blackman–Harris", "Bartlett"}

WindowNames are the windows in Window order.

Functions

func Factor

func Factor(n int) string

Factor describes n as a product of primes ("2^4 · 3^4", "prime") — which decides how go-fft takes it: lengths whose prime factors are all at most 13 run on the mixed-radix Stockham engine, the others through Rader's or Bluestein's algorithm.

func FormatSamples

func FormatSamples(x []float64) string

FormatSamples writes samples back as text ParseSamples reads, eight to a line, each with the shortest representation that round-trips.

func Generate

func Generate(p SignalParams) []float64

Generate returns the N samples of a preset signal:

  • sines: A1·sin(2π·F1·t) + A2·sin(2π·F2·t)
  • chirp: A1·sin(2π·(F1·t + (F2−F1)·t²/(2T))), a linear sweep over T = N/Fs
  • square: ±A1 at F1 (the sign of sin(2π·F1·t), +A1 at zero)
  • noise: Gaussian noise of standard deviation A1, plus A2·sin(2π·F1·t)
  • impulse: A1 at n = 0, zero elsewhere

with t = n/Fs. The noise is seeded, so a configuration always draws the same samples. PresetCustom has no generator: it returns nil.

func GoCode

func GoCode(c Config, n int) string

GoCode is the Go program fragment that computes what c computes on an n-sample input x — the exact go-fft calls, so a visitor can copy it.

func NumpyCode

func NumpyCode(c Config, n int) string

NumpyCode is the numpy / scipy.fft call that computes the same thing.

func ParseSamples

func ParseSamples(s string) ([]float64, error)

ParseSamples reads numbers separated by commas, semicolons, spaces or new lines — what a pasted numpy array, CSV column or Go slice literal looks like. Brackets, braces and a leading "[]float64" are ignored, so `[1, 2, 3]` and `[]float64{1, 2, 3}` both parse. A line starting with # is a comment.

func SetupText

func SetupText(scale float64)

SetupText installs the toolkit's metric scale and an anti-aliased face at that scale. The wasm shell calls it with window.devicePixelRatio BEFORE NewScene, so every metric the layout reads is already scaled: one logical pixel is one CSS pixel, drawn at device resolution.

func Smooth

func Smooth(n int) bool

Smooth reports whether every prime factor of n is at most 13 — a length the Stockham engine takes directly.

Types

type Config

type Config struct {
	Transform Transform
	Type      int // DCT/DST type, 1..4
	Norm      fft.Norm
	Window    Window
	Precision Precision
	Shift     bool // FFTShift the FFT's output and its frequencies
	Fs        float64
}

Config is everything that decides what transform runs on the input.

func (Config) Typed

func (c Config) Typed() bool

Typed reports whether the configured transform takes a type.

func (Config) Validate

func (c Config) Validate(n int) error

Validate reports why c cannot run on n samples, or nil.

type Curve

type Curve struct {
	Label string
	X, Y  []float64
	Style CurveStyle
}

Curve is one plotted series, in the view-model's own terms: the View turns it into a toolkit plot series. The view-model imports no widget.

type CurveStyle

type CurveStyle int

CurveStyle is how a curve is drawn: joined, or as stems from zero.

const (
	CurveLine CurveStyle = iota
	CurveStem
)

type Palette

type Palette struct {
	Background, Surface, Ink, Line, Border, Accent toolkit.RGBA
}

Palette is the handful of colours the page's stylesheet defines; the host reads them from the CSS custom properties, so the canvas wears exactly the landing's light or dark theme and its brand accent.

func (Palette) Theme

func (p Palette) Theme() *toolkit.Theme

Theme is the toolkit theme the palette paints with.

type Precision

type Precision int

Precision is the floating-point width the transform runs in.

const (
	Float64 Precision = iota
	Float32
)

type Preset

type Preset int

Preset names a built-in input signal.

const (
	PresetSines Preset = iota
	PresetChirp
	PresetSquare
	PresetNoise
	PresetImpulse
	PresetCustom
)

type Result

type Result struct {
	// Input is what was transformed: the samples after the window, or for
	// IRFFT the N/2+1 values taken as a half spectrum.
	Input []float64
	// Re and Im are the output; Im is nil for a real-valued transform.
	Re, Im []float64
	// Axis is the abscissa of each output value, and AxisName its unit:
	// frequency in Hz for a forward FFT/RFFT, time in seconds for the
	// inverse FFTs, the coefficient index k for the DCT and DST.
	Axis     []float64
	AxisName string
	// RoundTrip is the largest |input − inverse(forward(input))|, the
	// inverse being run with the same Norm and precision.
	RoundTrip float64
}

Result is one run of a transform.

func Run

func Run(x []float64, c Config) (Result, error)

Run transforms x as c says, then inverts the result to measure the round trip. x is not modified.

type Scene

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

Scene is the View: the toolkit widget tree, bound to a ViewModel, and the small amount of host bookkeeping a canvas application needs (the size, the pointer capture, a repaint flag). It implements go-widgets/webcanvas's App, Resizer, Scroller and Animator, so the wasm shell is one webcanvas.Run call.

func NewScene

func NewScene(vm *ViewModel, w, h int) *Scene

NewScene builds the widget tree for a w×h device-pixel surface and binds it to vm. Call SetupText first.

func (*Scene) AnimationStep

func (s *Scene) AnimationStep(float64) bool

AnimationStep repaints when something outside an input event changed the scene — the theme, a paste, a binding — and costs nothing otherwise.

func (*Scene) Char

func (s *Scene) Char(ch string) bool

Char is typed text.

func (*Scene) Click

func (s *Scene) Click(x, y int) bool

Click is a primary-button press at device pixel (x, y).

func (*Scene) Context

func (s *Scene) Context(int, int) bool

Context is a secondary click; the playground has no context menu.

func (*Scene) Draw

func (s *Scene) Draw(buf []byte)

Draw paints the whole surface into buf (RGBA, row-major).

func (*Scene) KeyDown

func (s *Scene) KeyDown(code string) bool

KeyDown is a named key. A single character here is a shortcut (the shell sends ⌘V as "v"): it is left to the browser, which turns copy and paste into the events the host forwards to Paste.

func (*Scene) Move

func (s *Scene) Move(x, y int) bool

Move is pointer motion: a drag to the pressed widget, else hover.

func (*Scene) Paste

func (s *Scene) Paste(text string) bool

Paste inserts text from the system clipboard where the keyboard is: the sample editor takes it whole (a pasted column of numbers), a field takes its first line.

func (*Scene) Rects

func (s *Scene) Rects() map[string]toolkit.Rect

Rects names the on-screen rectangle of each control, in device pixels, for a test harness that drives the page with synthetic events.

func (*Scene) Release

func (s *Scene) Release(x, y int) bool

Release ends a press.

func (*Scene) Resize

func (s *Scene) Resize(cw, ch int) (int, int)

Resize relays the scene out for a canvas of cw×ch CSS pixels, returning the device-pixel size it renders at (the CSS size times the metric scale).

func (*Scene) Scroll

func (s *Scene) Scroll(x, y, _, dy int) bool

Scroll is the wheel, in rows.

func (*Scene) SetPalette

func (s *Scene) SetPalette(p Palette)

SetPalette recolours the scene: the host calls it at start and whenever the page's theme flips.

func (*Scene) Size

func (s *Scene) Size() (int, int)

Size is the surface size in device pixels.

func (*Scene) Theme

func (s *Scene) Theme() *toolkit.Theme

Theme is the toolkit theme the scene is painted with.

type SignalParams

type SignalParams struct {
	Preset Preset
	N      int
	Fs     float64
	F1, F2 float64
	A1, A2 float64
	Seed   uint64
}

SignalParams describes a generated signal. N samples taken at Fs Hz; F1, F2, A1 and A2 are read by each preset as its doc says.

type Timing

type Timing struct {
	PerCall time.Duration
	Runs    int
}

Timing is how long one transform took, measured over Runs repetitions.

func Time

func Time(x []float64, c Config, budget time.Duration, now func() time.Time) (Timing, error)

Time runs the forward transform of c on x repeatedly — doubling the count until the batch takes at least budget — and reports the time per call. A single call to a tiny transform is far below the browser clock's resolution (performance.now is coarsened to 0.1 ms or worse), so one run would measure the clock, not the transform. now is the clock (time.Now in production).

type Transform

type Transform int

Transform names a transform the playground runs.

const (
	TransformFFT Transform = iota
	TransformIFFT
	TransformRFFT
	TransformIRFFT
	TransformDCT
	TransformIDCT
	TransformDST
	TransformIDST
)

func (Transform) ComplexOut

func (t Transform) ComplexOut() bool

ComplexOut reports whether t returns complex values.

func (Transform) Inverse

func (t Transform) Inverse() bool

Inverse reports whether t maps a spectrum back to samples.

func (Transform) Typed

func (t Transform) Typed() bool

Typed reports whether t takes a DCT/DST type (I to IV).

type View

type View int

View names how a complex output is shown.

const (
	ViewMagnitude View = iota
	ViewDecibel
	ViewPhase
	ViewReIm
)

type ViewModel

type ViewModel struct {
	// The signal.
	Preset  *mvvm.Observable[int]
	N       *mvvm.Observable[string]
	Fs      *mvvm.Observable[string]
	F1, F2  *mvvm.Observable[string]
	A1, A2  *mvvm.Observable[string]
	Samples *mvvm.Observable[string]

	// The transform.
	Transform *mvvm.Observable[int]
	Type      *mvvm.Observable[int]
	Window    *mvvm.Observable[int]
	Norm      *mvvm.Observable[int]
	Precision *mvvm.Observable[int]
	View      *mvvm.Observable[int]
	Shift     *mvvm.Observable[bool]

	// Derived: written by recompute alone.
	InputCurves  *mvvm.ObservableList[Curve]
	OutputCurves *mvvm.ObservableList[Curve]
	InputAxis    *mvvm.Observable[string]
	OutputAxis   *mvvm.Observable[string]
	OutputTitle  *mvvm.Observable[string]
	Length       *mvvm.Observable[string] // "N = 1009 (prime) · Rader/Bluestein"
	Accuracy     *mvvm.Observable[string] // the round trip
	Speed        *mvvm.Observable[string] // the timing
	Problem      *mvvm.Observable[string] // why nothing could be computed, or ""
	GoCode       *mvvm.Observable[string]
	NumpyCode    *mvvm.Observable[string]

	// Commands.
	CopyGo    *mvvm.Command
	CopyNumpy *mvvm.Command

	// Clipboard receives the text a Copy command copies; the host installs
	// it (navigator.clipboard in a browser). Nil copies nothing.
	Clipboard func(string)
	// Now is the clock the timing reads (time.Now unless a test swaps it).
	Now func() time.Time
	// Budget is how long the timing keeps repeating a transform.
	Budget time.Duration
	// contains filtered or unexported fields
}

ViewModel holds every piece of the playground's state as go-widgets/mvvm observables, and derives the plots, the numbers and the code from them. It references no widget; scene.go binds the widgets to it.

func NewViewModel

func NewViewModel() *ViewModel

NewViewModel builds the view-model with the opening configuration — two sines in 1 000 samples at 1 kHz, an RFFT, a Hann window — and computes it.

func (*ViewModel) Config

func (vm *ViewModel) Config() Config

Config is the transform configuration the pickers describe.

type Window

type Window int

Window names a window applied to the input before the transform.

const (
	WindowNone Window = iota
	WindowHann
	WindowHamming
	WindowBlackman
	WindowBlackmanHarris
	WindowBartlett
)

func (Window) Coefficients

func (w Window) Coefficients(n int) []float64

Coefficients returns the window's n coefficients, from go-fft; nil for none.

Directories

Path Synopsis
cmd
playground-wasm command
The real entry point is wasm-only (main.go); this stub keeps `go build ./...` and `go test ./...` green on every native host.
The real entry point is wasm-only (main.go); this stub keeps `go build ./...` and `go test ./...` green on every native host.

Jump to

Keyboard shortcuts

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