ntcharts - Nimble Terminal Charts

April 30, 2026 · View on GitHub

Build Status Latest Release GoDoc Code Of Conduct

ntcharts is a Golang Terminal Charting library for the Bubble Tea Framework and other TUIs.

We supply many chart types within the glory of your terminal!

TypeDescription
CanvasA 2D grid to plot arbitrary runes, with LipGloss for styling and BubbleZone for mousing. It is the foundation for all the following charts.
Bar ChartDisplays values as either horizontal rows or vertical columns.
Heat MapDisplays (x,y) values on a color-mapped heatmap.
Line ChartDisplays (X,Y) data points onto a 2D grid in various types of charts.
OHLC/Candle ChartDisplays Open, High, Low, Close values as candlesticks.
PictureDisplays images with picture and via http with pictureurl
Chart PictureRenders go-analyze/charts chart images via an embedded picture.Model — Kitty graphics with glyph fallback.
Heat PictureHigh-resolution continuous-field heatmap via an embedded picture.Model. Sampler-driven; Kitty-mode samples at full pixel resolution for smooth gradients.
Scatter ChartPlots abitrary runes onto (X,Y) coordinates.
Streamline ChartDisplays a continuous a line moving across the Canvas from the right side to the left side.
Time Series ChartDisplays lines with values on the Y axis and time values on the X axis.
Waveline ChartA line chart that connects points in a wave pattern.
SparklineA small, simple visual of data chart for quick understanding.

Quickstart Tutorial

This tutorial creates a simple Time Series Chart with two data sets utilizing the Bubble Tea framework, Lip Gloss for styling and BubbleZone for mouse support.

quickstart gif

Demo Apps

Standalone CLI demos. Run task to build them all into the bin/ directory.

CommandSourceWhat it shows
ntcharts-quickstartexamples/quickstartThe tutorial above — time-series chart with two data sets, mouse support.
ntcharts-ohlccmd/ntcharts-ohlcRenders OHLC candles + a sparkline from a CSV (example.csv) using the time-series line chart with braille runes.
ntcharts-lorem-picsumcmd/ntcharts-lorem-picsumSortable/filterable Lorem Picsum catalog browser; previews the selected image side-by-side in Glyph and Kitty graphics modes via pictureurl. Requires a Kitty-graphics-capable terminal for the right pane.
ntcharts-pictureexamples/pictureTwo-pane image demo: embedded PNG via picture on the left, fetched URL via pictureurl on the right.
ntcharts-chartpictureexamples/chartpictureLive-updating chart rendered through chartpicture (go-analyze/charts → image → Kitty/Glyph). Press r to swap line/bar, t to cycle themes, g to toggle modes.
ntcharts-heatpicture-perlinexamples/heatpicture/perlinAnimated 2D Perlin-noise field rendered through heatpicture at full Kitty-graphics resolution (smooth sub-cell gradients) with Glyph fallback. <space> start/stop, t toggle modes, F cycle sampling factor.

ntcharts-lorem-picsum gif

ntcharts-ohlc gif

BubbleTea Version Compatibility

We have migrated to Bubble Tea v2. It exists on the v2 branch. You should import as so:

import "github.com/NimbleMarkets/ntcharts/v2"

Our Bubble Tea v1 compatible library exists on the main branch. You should import it as so:

import "github.com/NimbleMarkets/ntcharts"

We will continue to backport relevant fixes to both branches.

Please note that the v2 designation is for BubbleTea API compatibility. Despite the version number, the ntcharts API is still subject to change. v2 is the primary development branch branch now.

Usage

See the examples folder for code samples and visuals of each type.

Canvas

package main

import (
    "fmt"
    "github.com/NimbleMarkets/ntcharts/v2/canvas"
    "charm.land/lipgloss/v2"
)

func main() {
    c := canvas.New(5, 2)
    c.SetLinesWithStyle(
        []string{"hello", "world"},
        lipgloss.NewStyle().Foreground(lipgloss.Color("6"))) // cyan

    fmt.Println(c.View())
}

This example produces the following canvas with Lip Gloss foreground color:

canvas png

Bar Chart

package main

import (
    "fmt"
    "github.com/NimbleMarkets/ntcharts/v2/barchart"
    "charm.land/lipgloss/v2"
)

func main() {
    d1 := barchart.BarData{
        Label: "A",
        Values: []barchart.BarValue{
            {"Item1", 21.2, lipgloss.NewStyle().Foreground(lipgloss.Color("10"))}}, // green
    }
    d2 := barchart.BarData{
        Label: "B",
        Values: []barchart.BarValue{
            {"Item1", 15.2, lipgloss.NewStyle().Foreground(lipgloss.Color("9"))}}, // red
    }

    bc := barchart.New(11, 10)
    bc.PushAll([]barchart.BarData{d1, d2})
    bc.Draw()

    fmt.Println(bc.View())
}

This example produces the following bar chart with green and red bars:

barchart png

Streamline Chart

package main

import (
    "fmt"
    "github.com/NimbleMarkets/ntcharts/v2/linechart/streamlinechart"
)

func main() {
    slc := streamlinechart.New(13, 10)
    for _, v := range []float64{4, 6, 8, 10, 8, 6, 4, 2, 0, 2, 4} {
        slc.Push(v)
    }
    slc.Draw()

    fmt.Println(slc.View())
}

This example produces the following streamline chart:

  │  ╭╮      
 8│  ││      
  │ ╭╯╰╮     
 6│ │  │     
  │╭╯  ╰╮    
 4├╯    ╰╮  ╭
  │      │  │
 2│      ╰╮╭╯
  │       ││ 
 0│       ╰╯ 

Time Series Chart

package main

import (
    "fmt"
    "time"
    "github.com/NimbleMarkets/ntcharts/v2/linechart/timeserieslinechart"
)

func main() {
    tslc := timeserieslinechart.New(41, 10)
    for i, v := range []float64{0, 4, 8, 10, 8, 4, 0, -4, -8, -10, -8, -4, 0} {
        date := time.Now().Add(time.Hour * time.Duration(24*i))
        tslc.Push(timeserieslinechart.TimePoint{date, v})
    }
    tslc.DrawBraille()

    fmt.Println(tslc.View())
}

This example produces the following time series chart using braille runes starting with today's date:

 10│      ⣀⠤⠒⠉⠒⠤⡀                        
   │    ⡠⠊      ⠈⠢⡀                      
  5│  ⡠⠊          ⠈⠢⡀                    
   │⡠⠊              ⠈⠑⢄                 ⢀
  0│                   ⠑⡄              ⡔⠁
   │                    ⠈⠢⡀          ⡠⠊  
 -5│                      ⠈⠢⡀      ⡠⠊    
   │                        ⠈⠑⠢⢄⡠⠔⠊      
-10└─────────────────────────────────────
   '24 03/27   03/31   04/03   04/05     

Waveline Chart

package main

import (
    "fmt"
    "github.com/NimbleMarkets/ntcharts/v2/canvas"
    "github.com/NimbleMarkets/ntcharts/v2/linechart/wavelinechart"
)

func main() {
    wlc := wavelinechart.New(12, 10, wavelinechart.WithYRange(-3, 3))
    wlc.Plot(canvas.Float64Point{1.0, 2.0})
    wlc.Plot(canvas.Float64Point{3.0, -2.0})
    wlc.Plot(canvas.Float64Point{5.0, 2.0})
    wlc.Plot(canvas.Float64Point{7.0, -2.0})
    wlc.Plot(canvas.Float64Point{9.0, 2.0})
    wlc.Draw()

    fmt.Println(wlc.View())
}

This example produces the following waveline chart:

 3│         
  │╭╮  ╭╮  ╭
 2│││  ││  │
  │││  ││  │
 0├╯╰╮╭╯╰╮╭╯
  │  ││  ││ 
-2│  ││  ││ 
  │  ╰╯  ╰╯ 
-3└─────────
  0 2 4 6    

Sparkline

package main

import (
    "fmt"
    "github.com/NimbleMarkets/ntcharts/v2/sparkline"
)

func main() {
    sl := sparkline.New(10, 5)
    sl.PushAll([]float64{7.81, 3.82, 8.39, 2.06, 4.19, 4.34, 6.83, 2.51, 9.21, 1.3})
    sl.Draw()

    fmt.Println(sl.View())
}

This example produces the following sparkline:

sparkline png

Heat Map

Heat Maps map values to colors on a 2D grid. The following example creates a heatmap of the function sin(sqrt(x^2 + y^2)). There are more examples in the examples README.

package main

import (
	"fmt"
	"math"

	"github.com/NimbleMarkets/ntcharts/v2/heatmap"
)

func main() {
	hm := heatmap.New(20, 20, heatmap.WithValueRange(0, 1))
	hm.SetXYRange(-1, 1, -1, 1)
	for x := float64(-1); x < 1.0; x += 1.0 / float64(hm.GraphWidth()) {
		for y := float64(-1); y < 1.0; y += 1.0 / float64(hm.GraphHeight()) {
			val := math.Sin(math.Sqrt(x*x + y*y))
			hm.Push(heatmap.NewHeatPoint(x, y, val))
		}
	}
	hm.Draw()
	fmt.Println(hm.View())
}

This example (source) produces the following heatmap:

simple heatmap png

Open Collaboration

We welcome contributions and feedback. Please adhere to our Code of Conduct when engaging our community.

Acknowledgements

Thanks to Charm.sh for making the command line glamorous and sharing Bubble Tea and Lip Gloss and more. Thanks to BubbleZone for bringing the mouse support :mouse:.

Thanks also to asciigraph, ratatui, and termdash for inspiration.

License

This project is released under the MIT License, see LICENSE.txt, except for the following files:

Copyright (c) 2024-2026 Neomantra Corp.


Made with :heart: and :fire: by the team behind Nimble.Markets.