MIT licensed by Michael Chavinda
Maintained by [email protected]
This version can be pinned in stack with:granite-0.3.0.1@sha256:428e50f835333886d407c40e56070a3d304a0866f893a08bc40308a2fde5cf6a,1951

Module documentation for 0.3.0.1

Depends on 3 packages(full list with versions):
Used by 1 package in nightly-2025-09-18(full list with versions):

Granite

A library for producing terminal plots. It depends only on Haskell’s base and text (only for efficiency but it could use String. In fact, we expose an API that uses String for easier use in GHCi) packages so it should be easy to use and install.

Supported graph types

  • Scatter plots
  • Histograms
  • (Stacked) bar charts
  • Pie charts
  • Box plots
  • Line charts
  • Heat maps

Examples

Scatter plot

Scatter Plot

import           Control.Monad
import           System.Random.Stateful

import Granite.String

main :: IO ()
main = do
  g <- newIOGenM =<< newStdGen
  let range = (0 :: Double, 1 :: Double)
  ptsA_x <- replicateM 600 (uniformRM range g)
  ptsA_y <- replicateM 600 (uniformRM range g)
  ptsB_x <- replicateM 600 (uniformRM range g)
  ptsB_y <- replicateM 600 (uniformRM range g)
  putStrLn (scatter "Random points" [series "A" (zip ptsA_x ptsA_y), series "B" (zip ptsB_x ptsB_y)]
            defPlot{widthChars=68,heightChars=22,plotTitle="Random points"})

Bar chart

Bar chart

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = T.putStrLn (bars [("Q1",12),("Q2",18),("Q3",9),("Q4",15)] defPlot {plotTitle="Sales"})

Stacked bar chart

Stacked bar chart

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main =  T.putStrLn (stackedBars [ ("Q1", [("Hardware", 120), ("Software", 200), ("Services", 80)])
                                , ("Q2", [("Hardware", 135), ("Software", 220), ("Services", 95)])
                                , ("Q3", [("Hardware", 110), ("Software", 240), ("Services", 110)])
                                , ("Q4", [("Hardware", 145), ("Software", 260), ("Services", 125)])
                                ] defPlot {plotTitle="Quarterly Revenue Breakdown"})

Pie chart

Pie chart

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = T.putStrLn (pie [("Alpha",0.35),("Beta",0.25),("Gamma",0.20),("Delta",0.20)] defPlot{widthChars=46,heightChars=18,legendPos=LegendRight,plotTitle="Share"})

Box plot

Box plot

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = T.putStrLn $ boxPlot [ ("Class A", [78, 82, 85, 88, 90, 92, 85, 87, 89, 91, 76, 94, 88])
                            , ("Class B", [70, 75, 72, 80, 85, 78, 82, 77, 79, 81, 74, 83])
                            , ("Class C", [88, 92, 95, 90, 93, 89, 91, 94, 96, 87, 90, 92])
                            , ("Class D", [65, 70, 72, 68, 75, 80, 73, 71, 69, 74, 77, 76])
                            ] defPlot {plotTitle="Test Score Distribution by Class"}

Line graph

Line graph

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = T.putStrLn $ lineGraph [ ("Product A", [(1, 100), (2, 120), (3, 115), (4, 140), (5, 155), (6, 148)])
                                              , ("Product B", [(1, 80), (2, 85), (3, 95), (4, 92), (5, 110), (6, 125)])
                                              , ("Product C", [(1, 60), (2, 62), (3, 70), (4, 85), (5, 82), (6, 90)])
                                              ] defPlot {plotTitle="Monthly Sales Trends"}

Heatmap

Heatmap

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = do
  let matrix = [ [1.0,  0.8,  0.3, -0.2,  0.1]
               , [0.8,  1.0,  0.5, -0.1,  0.2]
               , [0.3,  0.5,  1.0,  0.6,  0.4]
               , [-0.2, -0.1, 0.6,  1.0,  0.7]
               , [0.1,  0.2,  0.4,  0.7,  1.0]
               ]
  T.putStrLn $ heatmap matrix defPlot {plotTitle="Correlation Matrix"}

Histogram

Histogram

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T

import Granite

main :: IO ()
main = do
  heights <- replicateM 5000 (uniformRM (160 :: Double, 190 :: Double) g)
  Text.putStrLn $
      histogram
          (bins 30 155 195)
          heights
          defPlot
              { widthChars = 68
              , heightChars = 18
              , legendPos = LegendBottom
              , xFormatter = \_ _ v -> Text.pack (show (round v :: Int))
              , xNumTicks = 10
              , yNumTicks = 5
              , plotTitle = "Heights (cm)"
              }

Changes

Revision history for granite

0.3.0.1 – 2025-09-17

  • Left edge of plots now has an elbow instead of disjoint bars.
  • Remove dependency on random.

0.3.0.0 – 2025-08-31

  • Export a LabelFormatter function along with AxisEnv to define smarter labels.
  • Change API of formatter to use AxisEnv and slot budget.
  • Users can now specify the number of ticks to show and the colour palette for a plot.

0.2.0.2 – 2025-08-30

  • Add plot option to define the format of labels on both axes.

0.2.0.1 – 2025-08-26

  • Loosen bounds for text

0.2.0.0 – 2025-08-26

  • Plot title is now part of the configuration options.
  • API now uses Text instead of String.
  • You can now specify the bounds of a plot e.g. T.putStrLn $ lineGraph [("Foo", [(0, 0), (10, 1)]), ("Bar", [(0, 1), (10, 0)])] defPlot { xBounds = (Just 0, Just 10), yBounds = (Just 0, Just 1) }

0.1.0.3 – 2025-08-21

  • Fix heatmaps: matrix grid wasn’t properly mapping to canvas + axes were reversed.

0.1.0.2 – 2025-08-20

  • Add README to doc files.

0.1.0.1 – 2025-08-20

  • Remove IO monad from plotting functions.
  • Faster plotting after moving from lists as arrays to AVL trees.
  • All functions are now strict by default.

0.1.0.0 – 2025-08-18

  • Support for pie charts, bar charts, heatmaps, line graphs, box plots, and histograms.