Skip to content
glyphcss

ASCII and braille charts for chat, terminals, and the web

@glyphcss/charts draws charts into a cell canvas and returns text or escaped HTML spans. Display the result in a monospace <pre>; no browser is needed to generate a static chart.

Terminal window
npm install @glyphcss/charts
import { glyphChartLine, glyphChartPlot, renderGlyphChart } from "@glyphcss/charts";
const spec = glyphChartPlot({
title: "Daily orders vs plan",
marks: [
glyphChartLine([18, 24, 21, 35, 42, 39, 48, 44, 38, 52, 61, 57], {}, { name: "Orders" }),
glyphChartLine([30, 32, 34, 38, 44, 46, 50, 52, 54, 58, 64, 66], {}, { name: "Plan" }),
],
axes: { x: { title: "Day (index)" }, y: { title: "Orders" } },
});
const result = renderGlyphChart(spec, {
target: "chat",
width: 64,
height: 18,
});
console.log(result.text);
                      Daily orders vs plan                      
                                                                
Orders                                                          
                                                                
   │                                                     ▗▄▄▄▄▄▄
60 ┤                                                  ▗▄▀▚▄▄▖   
   │                                          ▄▄▄▄▄▄▄▀▚▄▀▘  ▝▀▀▄
50 ┤                               ▄▄▄▄▄▄▄▄▞▀▀      ▄▀▘         
   │                         ▗▄▄▞▀▀ ▄▄▄           ▗▞            
40 ┤                    ▄▄▟█▀▘   ▗▞▀   ▀▀▀▄▄▖    ▞▘             
   │               ▄▄▞▀▀    ▀▀▀▄▀▘          ▝▀▀▄▀               
30 ┤▗▄▄▄▄▄▄▄▄▄▄▄▞▀▜▞                                            
   │             ▞▘                                             
   │    ▄▄▄▄▄▄▄▄▀                                               
20 └▗▞▀▀───────┬─────────┬──────────┬──────────┬──────────┬─────
    0          2         4          6          8         10     
                            Day (index)                         
         ▗▄▄ Orders                       ▗▖▄ Plan              
Code
import { renderGlyphChart } from "@glyphcss/charts";

const { text } = renderGlyphChart({
  marks: [
    {
      type: "line",
      data: [18, 24, 21, 35, 42, 39, 48, 44, 38, 52, 61, 57],
      channels: {},
      options: {
        name: "Orders"
      }
    },
    {
      type: "line",
      data: [30, 32, 34, 38, 44, 46, 50, 52, 54, 58, 64, 66],
      channels: {},
      options: {
        name: "Plan"
      }
    }
  ],
  title: "Daily orders vs plan",
  axes: {
    x: {
      title: "Day (index)"
    },
    y: {
      title: "Orders"
    }
  }
}, {
  target: "chat",
  charset: "blocks",
  width: 64,
  height: 18
});
console.log(text);

A bare number array also works: renderGlyphChart([18, 24, 21]) creates one line mark. Use a spec when marks share axes, scales, or a title.

These previews use the same data at 60 × 12 cells. Terminal color is shown with CSS here; the terminal API default is ANSI truecolor. Chat defaults to plain box drawing because chat fonts misalign braille; this preview passes charset: "blocks", which most chat fonts also draw cleanly. Terminal and web use braille.

60 ┤                                                  ▄▄▖   
   │                                               ▗▞▀  ▝▀▀▄
50 ┤                                             ▄▀▘        
   │                              ▄▄▖          ▗▞           
40 ┤                   ▄▄▄▖    ▗▞▀  ▝▀▀▄▄▖    ▞▘            
   │               ▄▞▀▀   ▝▀▀▄▀▘         ▝▀▀▄▀              
30 ┤             ▗▞                                         
   │            ▞▘                                          
   │    ▄▄▄▄▄▄▄▀                                            
20 └▗▞▀▀──────┬─────────┬─────────┬─────────┬─────────┬─────
    0         2         4         6         8        10     
                       ▗▄▄ Orders                           
Code
import { renderGlyphChart } from "@glyphcss/charts";

const { text } = renderGlyphChart({
  type: "line",
  data: [18, 24, 21, 35, 42, 39, 48, 44, 38, 52, 61, 57],
  channels: {},
  options: {
    name: "Orders"
  }
}, {
  target: "chat",
  color: "none",
  charset: "blocks",
  width: 60,
  height: 12
});
console.log(text);
60 ┤                                                  ⡠⢄⡀   
   │                                               ⢀⠔⠉  ⠈⠉⠒⠤
50 ┤                                             ⡠⠊⠁        
   │                              ⡠⢄⡀          ⢀⠜           
40 ┤                   ⣀⠤⢄⡀    ⢀⠔⠉  ⠈⠉⠒⠤⢄⡀    ⡔⠁            
   │               ⡠⠔⠊⠉   ⠈⠉⠒⠤⠊⠁         ⠈⠉⠒⠤⠊              
30 ┤             ⢀⠜                                         
   │            ⡔⠁                                          
   │    ⣀⠤⠤⠤⠤⠤⠤⠊                                            
20 └⠠⠔⠊⠉──────┬─────────┬─────────┬─────────┬─────────┬─────
    0         2         4         6         8        10     
                       ⠠⠤⠤ Orders                           
Code
import { renderGlyphChart } from "@glyphcss/charts";

const { text } = renderGlyphChart({
  type: "line",
  data: [18, 24, 21, 35, 42, 39, 48, 44, 38, 52, 61, 57],
  channels: {},
  options: {
    name: "Orders"
  }
}, {
  target: "terminal",
  width: 60,
  height: 12
});
console.log(text);
60 ┤                                                  ⡠⢄⡀   
   │                                               ⢀⠔⠉  ⠈⠉⠒⠤
50 ┤                                             ⡠⠊⠁        
   │                              ⡠⢄⡀          ⢀⠜           
40 ┤                   ⣀⠤⢄⡀    ⢀⠔⠉  ⠈⠉⠒⠤⢄⡀    ⡔⠁            
   │               ⡠⠔⠊⠉   ⠈⠉⠒⠤⠊⠁         ⠈⠉⠒⠤⠊              
30 ┤             ⢀⠜                                         
   │            ⡔⠁                                          
   │    ⣀⠤⠤⠤⠤⠤⠤⠊                                            
20 └⠠⠔⠊⠉──────┬─────────┬─────────┬─────────┬─────────┬─────
    0         2         4         6         8        10     
                       ⠠⠤⠤ Orders                           
Code
import { renderGlyphChart } from "@glyphcss/charts";

const { text } = renderGlyphChart({
  type: "line",
  data: [18, 24, 21, 35, 42, 39, 48, 44, 38, 52, 61, 57],
  channels: {},
  options: {
    name: "Orders"
  }
}, {
  target: "web",
  width: 60,
  height: 12
});
console.log(text);