Yar Kravtsov

pin: A Small CLI Spinner for Go

· go

Most Go CLI spinner libraries have the same problem: they want to be frameworks. You pull in one dependency for a tiny rotating character in your terminal, and suddenly you’re carrying transitive dependencies, ANSI abstractions you didn’t ask for, and an API surface that assumes you’re building a dashboard.

All I wanted was a dot that spins.

So I built pin

pin is a terminal spinner for Go. It does colors, prefixes, separators, and live message updates. That’s it. The important part is that it uses only the Go standard library. Zero external dependencies.

Why does that matter? Because dependencies are promises. Every go get you run is a bet that some stranger’s weekend project will keep working the way you expect. For something as trivial as a spinner, that bet is genuinely not worth taking. pin is easy to audit, easy to vendor, and it will never surprise you with a breaking change three levels deep in your dependency tree.

What you get

  • Spinner color, text color, prefix, separator — the small formatting knobs you actually need.
  • Live message updates while work is in progress. Because “Loading…” isn’t helpful for thirty seconds straight.
  • Pipe detection. When stdout isn’t a terminal, pin disables animation so you don’t get escape codes in log files (try ./myapp | tee output.txt).
  • Standard library only. I keep saying it because that’s the point.

Installation

go get github.com/yarlson/pin

Quick start

package main

import (
	"context"
	"time"

	"github.com/yarlson/pin"
)

func main() {
	p := pin.New("Loading...",
		pin.WithSpinnerColor(pin.ColorCyan),
		pin.WithTextColor(pin.ColorYellow),
	)
	cancel := p.Start(context.Background())
	defer cancel()

	// Simulate work
	time.Sleep(3 * time.Second)

	p.UpdateMessage("Almost done...")
	time.Sleep(2 * time.Second)

	p.Stop("Done!")
}

Look, it’s a spinner. The API should fit in your head in thirty seconds, and this one does.

Fair question. If you’re already deep in a CLI framework that bundles its own spinner, use that. Seriously. But if you’re writing a small tool — a deployment script, a code generator, a quick utility — and you want a clean loading indicator without adopting someone else’s opinions about terminal rendering, pin exists for exactly that moment.

A spinner with no dependencies is a spinner that does not break because somebody else shipped a bad release on a Friday afternoon.

The source is on GitHub: github.com/yarlson/pin. It’s small enough that you can read the whole thing in one sitting.