Skip to content

blas0/guilty-spark

Repository files navigation

guilty-spark

Unicode character spinners for React, driven by requestAnimationFrame.

demo


Install

bun add guilty-spark
# or: npm i guilty-spark / pnpm add guilty-spark

React 18+ is a peer dependency.


Usage

import { Spinner, Loading, PRESETS } from 'guilty-spark'

// drop-in component
<Spinner {...PRESETS.BRAILLE_WAVE_3} />

// with label
<Loading {...PRESETS.DOTS_CYCLE} side="right">thinking</Loading>

// hook — access raw string or char array
const { raw, chars } = useSpinner(PRESETS.BLOCKS_WAVE_3)

Components

<Spinner>

<Spinner
  glyphs={GLYPHS.BLOCKS}    // required
  generator={sineWave(600)} // required
  width={5}                 // slots (default: 1)
  interval={80}             // ms per tick (overrides generator default)
  label="loading"           // aria-label (default: "loading")
  className="..."
  style={...}
/>

<Loading>

Wraps <Spinner> with a text label. side controls which side the spinner appears on.

<Loading {...PRESETS.BRAILLE_WAVE_3} side="right">thinking</Loading>
<Loading {...PRESETS.DOTS_CYCLE} side="left">loading</Loading>

Props: all SpinnerDef fields + children, side, className, style, spinnerClassName, spinnerStyle.


Presets

import { PRESETS } from 'guilty-spark'
Key Glyphs Generator Width
BRAILLE_WAVE_3 BRAILLE sineWave 3
BRAILLE_DENSITY BRAILLE densityCurve 1
DOTS_CYCLE DOTS staticFrames 1
DOTS_BOUNCE DOTS pingPong 1
BLOCKS_WAVE_3 BLOCKS sineWave 3
BLOCKS_WAVE_5 BLOCKS sineWave 5
BLOCKS_FILL BLOCKS pingPong 1
STARS_MORPH STARS densityCurve 1
CIRCLE_SPIN CIRCLE_QUAD staticFrames 1
DICE_BOUNCE DICE pingPong 1
ARROWS_SPIN ARROWS_8 staticFrames 1
TRIANGLE_SPIN TRIANGLES staticFrames 1

Presets are plain SpinnerDef objects — spread and override any field:

<Spinner {...PRESETS.BRAILLE_WAVE_3} width={7} interval={40} />

Glyphs

import { GLYPHS } from 'guilty-spark'

GLYPHS.BLOCKS        // [' ','▁','▂','▃','▄','▅','▆','▇','█']
GLYPHS.BRAILLE       // [' ','⠁','⠃','⠇','⠏','⠟','⠿','⣿']
GLYPHS.DOTS          // ['⁚','⁙','⁘','⁛','⁕','⁜','⁝','⁞']
GLYPHS.STARS         // ['⁎','⁑','⁂','※']
GLYPHS.CIRCLE_QUAD   // ['◴','◵','◶','◷']
GLYPHS.DICE          // ['⚀','⚁','⚂','⚃','⚄','⚅']
GLYPHS.ARROWS_4      // ['←','↑','→','↓']
GLYPHS.ARROWS_8      // ['←','↖','↑','↗','→','↘','↓','↙']
GLYPHS.TRIANGLES     // ['▲','▶','▼','◀']

Any readonly string[] works as a custom glyph set.


Generators

Factory Description Default interval
staticFrames(frames?) Steps through a frames array; slots show a sliding window 80 ms
densityCurve(density, steps?) Interpolates a [0,1] curve across the glyph set 80 ms
sineWave(period?) Continuous sine wave per slot, phase-offset for multi-slot travel every rAF frame

Utilities

pingPong(['a','b','c','d'])  // => ['a','b','c','d','c','b']  (bounce sequence)
loop(['a','b','c'])          // => ['a','b','c']              (forward cycle, explicit copy)

defineSpinner({ glyphs: GLYPHS.BRAILLE, generator: sineWave(800), width: 4 })

Custom composition

import { Spinner, defineSpinner, GLYPHS, sineWave, staticFrames, loop } from 'guilty-spark'

const MY_SPINNER = defineSpinner({
  glyphs: GLYPHS.BRAILLE,
  generator: staticFrames(loop([...GLYPHS.BRAILLE])),
  width: 1,
})

<Spinner {...MY_SPINNER} />

Custom generators implement GlyphGenerator:

import type { GlyphGenerator } from 'guilty-spark'

const random: GlyphGenerator = {
  initialState: 0,
  recommendedInterval: 120,
  nextState: (s) => s + 1,
  frame(_s, _i, _w, glyphs) {
    return glyphs[Math.floor(Math.random() * glyphs.length)]
  },
}

License

MIT

About

A TypeScript/React ESM library providing Unicode character-based spinner animations. Containing configurable glyph sets, generators, hooks, and preset components.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors