# asciigraph [![Build status][]][1] [![Go Report Card][]][2] [![Coverage Status][]][3] [![GoDoc][]][4] [![License][]][5] [![Mentioned in Awesome Go][]][6] Go package to make lightweight ASCII line graphs ╭┈╯. ![image][] ## Installation ```bash go get -u github.com/guptarohit/asciigraph@latest ``` ## Usage ### Basic graph ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := []float64{3, 4, 9, 6, 2, 4, 5, 8, 5, 10, 2, 7, 2, 5, 6} graph := asciigraph.Plot(data) fmt.Println(graph) } ``` Running this example would render the following graph: ```bash 10.00 ┤ ╭╮ 9.00 ┤ ╭╮ ││ 8.00 ┤ ││ ╭╮││ 7.00 ┤ ││ ││││╭╮ 6.00 ┤ │╰╮ ││││││ ╭ 5.00 ┤ │ │ ╭╯╰╯│││╭╯ 4.00 ┤╭╯ │╭╯ ││││ 3.00 ┼╯ ││ ││││ 2.00 ┤ ╰╯ ╰╯╰╯ ``` ### Multiple Series ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := [][]float64{{0, 1, 2, 3, 3, 3, 2, 0}, {5, 4, 2, 1, 4, 6, 6}} graph := asciigraph.PlotMany(data) fmt.Println(graph) } ``` Running this example would render the following graph: ```bash 6.00 ┤ ╭─ 5.00 ┼╮ │ 4.00 ┤╰╮ ╭╯ 3.00 ┤ │╭│─╮ 2.00 ┤ ╰╮│ ╰╮ 1.00 ┤╭╯╰╯ │ 0.00 ┼╯ ╰ ``` ### Custom Y-axis value formatting Use `YAxisValueFormatter(...)` to control how values printed on the Y-axis are rendered. This is useful for human-readable units like bytes, durations, or domain-specific labels. ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := []float64{ 30 * 1024 * 1024 * 1024, 70 * 1024 * 1024 * 1024, 2 * 1024 * 1024 * 1024, } graph := asciigraph.Plot(data, asciigraph.Height(5), asciigraph.Width(45), asciigraph.YAxisValueFormatter(func(v float64) string { return fmt.Sprintf("%.2f GiB", v/1024/1024/1024) }), ) fmt.Println(graph) } ``` Running this example would render the following graph: ```bash 70.00 GiB ┤ ╭──────╮ 56.40 GiB ┤ ╭───────╯ ╰────╮ 42.80 GiB ┤ ╭──────╯ ╰───╮ 29.20 GiB ┼──╯ ╰────╮ 15.60 GiB ┤ ╰───╮ 2.00 GiB ┤ ╰─ ``` ### X-axis Support Use `XAxisRange(min, max)` to add a labeled X-axis below the graph. `XAxisTickCount(n)` controls how many tick marks appear (default 5, minimum 2). ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := []float64{3, 4, 9, 6, 2, 4, 5, 8, 5, 10, 2, 7, 2, 5, 6} graph := asciigraph.Plot(data, asciigraph.XAxisRange(0, 14), asciigraph.XAxisTickCount(3), ) fmt.Println(graph) } ``` Running this example would render the following graph: ```bash 10.00 ┤ ╭╮ 9.00 ┤ ╭╮ ││ 8.00 ┤ ││ ╭╮││ 7.00 ┤ ││ ││││╭╮ 6.00 ┤ │╰╮ ││││││ ╭ 5.00 ┤ │ │ ╭╯╰╯│││╭╯ 4.00 ┤╭╯ │╭╯ ││││ 3.00 ┼╯ ││ ││││ 2.00 ┤ ╰╯ ╰╯╰╯ └┬──────┬──────┬ 0 7 14 ``` ### Colored graphs ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := make([][]float64, 4) for i := 0; i < 4; i++ { for x := -20; x <= 20; x++ { v := math.NaN() if r := 20 - i; x >= -r && x <= r { v = math.Sqrt(math.Pow(float64(r), 2)-math.Pow(float64(x), 2)) / 2 } data[i] = append(data[i], v) } } graph := asciigraph.PlotMany(data, asciigraph.Precision(0), asciigraph.SeriesColors( asciigraph.Red, asciigraph.Yellow, asciigraph.Green, asciigraph.Blue, )) fmt.Println(graph) } ``` Running this example would render the following graph: ![colored_graph_image][] ### Gradient (heatmap) coloring Instead of a solid color per series, `SeriesColorGradient` colors each point by its value along a palette — warm tones for high values, cool tones for low ones. The built-in `HeatmapSpectrum` provides a ready-made cool-to-warm palette, or you can pass your own color stops (lowest value first). ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1} graph := asciigraph.Plot(data, asciigraph.Height(10), asciigraph.SeriesColorGradient(asciigraph.HeatmapSpectrum...), ) fmt.Println(graph) } ``` Running this example would render the following graph: gradient graph ### Threshold coloring `ColorAbove` and `ColorBelow` highlight points that breach a threshold — useful for flagging alerts, like a CPU usage spike or a disk space warning — without recoloring the whole series. `ColorAbove` colors points strictly above its threshold (value > threshold) and `ColorBelow` strictly below (value < threshold). Points in between keep their normal color — the series color, or the gradient color when `SeriesColorGradient` is set. These take precedence over `SeriesColorGradient` and `SeriesColors`; when both thresholds match the same point, `ColorAbove` wins. ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" ) func main() { data := []float64{42, 48, 55, 81, 85, 91, 87, 34, 12, 17, 10, 18, 55, 50} graph := asciigraph.Plot(data, asciigraph.Height(10), asciigraph.Width(25), asciigraph.LowerBound(0), asciigraph.UpperBound(100), asciigraph.Caption("CPU usage % (red: critical, green: idle)"), asciigraph.ColorAbove(asciigraph.Red, 80), asciigraph.ColorBelow(asciigraph.Green, 25), ) fmt.Println(graph) } ``` Running this example would render the following graph — the spike above 80% in red and the dip below 25% in green: threshold colored graph On the CLI, the same thresholds are available via the `-ca` and `-cb` flags, each taking a `color,value` pair: ```bash seq 1 100 | asciigraph -h 10 -ca red,80 -cb green,25 ``` ### Legends for colored graphs The graph can include legends for each series, making it easier to interpret. ```go package main import ( "fmt" "github.com/guptarohit/asciigraph" "math" ) func main() { data := make([][]float64, 3) for i := 0; i < 3; i++ { for x := -12; x <= 12; x++ { v := math.NaN() if r := 12 - i; x >= -r && x <= r { v = math.Sqrt(math.Pow(float64(r), 2)-math.Pow(float64(x), 2)) / 2 } data[i] = append(data[i], v) } } graph := asciigraph.PlotMany(data, asciigraph.Precision(0), asciigraph.SeriesColors(asciigraph.Red, asciigraph.Green, asciigraph.Blue), asciigraph.SeriesLegends("Red", "Green", "Blue"), asciigraph.Caption("Series with legends")) fmt.Println(graph) } ``` Running this example would render the following graph: ![graph_with_legends_image][] ## CLI Installation This package also brings a small utility for command line usage. Assuming `$GOPATH/bin` is in your `$PATH`, install CLI with following command: ```bash go install github.com/guptarohit/asciigraph/cmd/asciigraph@latest ``` or pull Docker image: ```bash docker pull ghcr.io/guptarohit/asciigraph:latest ``` or download binaries from the [releases][] page. ## CLI Usage ```bash ✘ 0|125  16:19:23 > asciigraph --help Usage of asciigraph: asciigraph [options] Options: -ac axis color y-axis color of the plot -b buffer data points buffer when realtime graph enabled, default equal to `width` -c caption caption for the graph -ca above color points above a threshold: "color,value" (e.g. "red,4") -cb below color points below a threshold: "color,value" (e.g. "green,2") -cc caption color caption color of the plot -d delimiter data delimiter for splitting data points in the input stream (default ",") -f fps set fps to control how frequently graph to be rendered when realtime graph enabled (default 24) -g gradient gradient palette coloring points by value: "spectrum" for the built-in heatmap, or comma-separated color stops low to high (e.g. "blue,cyan,green") -h height height in text rows, 0 for auto-scaling -lb lower bound lower bound set the minimum value for the vertical axis (ignored if series contains lower values) (default +Inf) -lc label color y-axis label color of the plot -o offset offset in columns, for the label (default 3) -p precision precision of data point labels along the y-axis (default 2) -r realtime enables realtime graph for data stream -sc series colors comma-separated series colors corresponding to each series -sl series legends comma-separated series legends corresponding to each series -sn number of series number of series (columns) in the input data (default 1) -ub upper bound upper bound set the maximum value for the vertical axis (ignored if series contains larger values) (default -Inf) -w width width in columns, 0 for auto-scaling -xmax value x-axis maximum value (default NaN) -xmin value x-axis minimum value (default NaN) -xt tick count x-axis tick count (default 5, minimum 2) asciigraph expects data points from stdin. Invalid values are logged to stderr. ``` Feed it data points via stdin: ```bash seq 1 72 | asciigraph -h 10 -c "plot data from stdin" -xmin 0 -xmax 40 -xt 5 ``` or use Docker image: ```bash seq 1 72 | docker run -i --rm ghcr.io/guptarohit/asciigraph -h 10 -c "plot data from stdin" -xmin 0 -xmax 40 -xt 5 ``` Output: ```bash 72.00 ┤ ╭──── 64.90 ┤ ╭──────╯ 57.80 ┤ ╭──────╯ 50.70 ┤ ╭──────╯ 43.60 ┤ ╭──────╯ 36.50 ┤ ╭───────╯ 29.40 ┤ ╭──────╯ 22.30 ┤ ╭──────╯ 15.20 ┤ ╭──────╯ 8.10 ┤ ╭──────╯ 1.00 ┼──╯ └┬─────────────────┬─────────────────┬────────────────┬─────────────────┬ 0 10 20 30 40 plot data from stdin ``` Example of **real-time graph** for data points stream via stdin: Realtime graph for data points via stdin (google ping) using asciigraph
command for above graph ```sh ping -i.2 google.com | grep -oP '(?<=time=).*(?=ms)' --line-buffered | asciigraph -r -h 10 -w 40 -c "realtime plot data (google ping in ms) from stdin" ```
Example of **multi-series real-time graph** for data points stream via stdin: Ping latency comparison: Google (Blue) vs. DuckDuckGo (Red) with asciigraph
command for above graph ```sh {unbuffer paste -d, <(ping -i 0.4 google.com | sed -u -n -E 's/.*time=(.*)ms.*/\1/p') <(ping -i 0.4 duckduckgo.com | sed -u -n -E 's/.*time=(.*)ms.*/\1/p') } | asciigraph -r -h 15 -w 60 -sn 2 -sc "blue,red" -c "Ping Latency Comparison" -sl "Google, DuckDuckGo" ```
## Acknowledgement This package started as golang port of [asciichart][]. ## Contributing Feel free to make a pull request! :octocat: [Build status]: https://github.com/guptarohit/asciigraph/actions/workflows/test.yml/badge.svg [1]: https://github.com/guptarohit/asciigraph/actions/workflows/test.yml [Go Report Card]: https://goreportcard.com/badge/github.com/guptarohit/asciigraph [2]: https://goreportcard.com/report/github.com/guptarohit/asciigraph [Coverage Status]: https://coveralls.io/repos/github/guptarohit/asciigraph/badge.svg?branch=master [3]: https://coveralls.io/github/guptarohit/asciigraph?branch=master [GoDoc]: https://godoc.org/github.com/guptarohit/asciigraph?status.svg [4]: https://godoc.org/github.com/guptarohit/asciigraph [License]: https://img.shields.io/badge/licence-BSD-blue.svg [5]: https://github.com/guptarohit/asciigraph/blob/master/LICENSE [Mentioned in Awesome Go]: https://awesome.re/mentioned-badge-flat.svg [6]: https://github.com/avelino/awesome-go#advanced-console-uis [image]: https://user-images.githubusercontent.com/7895001/41509956-b1b2b3d0-7279-11e8-9d19-d7dea17d5e44.png [colored_graph_image]: https://user-images.githubusercontent.com/7895001/166443444-40ad8113-2c0f-46d7-9c75-1cf08435ce15.png [releases]: https://github.com/guptarohit/asciigraph/releases [asciichart]: https://github.com/kroitor/asciichart [graph_with_legends_image]: https://github.com/guptarohit/asciigraph/assets/7895001/4066ee95-55ca-42a4-8a03-e73ce20df5d3