
When you want to create a chart using modern libraries (like Vega-Lite, ECharts, or Chart.js), you face a fundamental trade-off:
SHORT specification → Default, uninspiring charts
DETAILED specification → Polished charts, but verbose, fragile, and error-prone
Think of it like giving directions:
For charts, those "detailed directions" include things like:
When AI agents (LLMs) try to write these detailed specifications:
Key Takeaway: There's a gap between "easy to write" and "looks good." Flint is designed to fill that gap.
Flint sits between what you write and what the chart library needs:
You write: [Simple, compact Flint spec]
↓
Flint Compiler: [Figures out all the details]
↓
Output: [Full, polished chart in Vega-Lite / ECharts / Chart.js]
Think of it like a smart translator that not only converts your words but also fills in context, grammar, and nuance automatically.
The word "intermediate" means Flint is not the final format — it's a middle step. You write in Flint's compact language, and the compiler produces the final, detailed format that the chart library understands.
Key Takeaway: Flint is a middle layer — compact on the input side, detailed and polished on the output side.
A semantic type goes beyond raw data format to capture meaning:
| Raw Data | Basic Type | Semantic Type |
|---|---|---|
2024-01 | String | Date/Period |
0.85 | Number | Percentage |
"USA" | String | Country |
4.2 | Number | Ranking |
-0.3 | Number | Correlation |
Once the system knows the meaning, it can make smart decisions automatically:
AI agents are actually good at inferring semantic types from:
newUsers, price, country)This is much easier for AI than figuring out exact pixel spacing or color hex codes.
Key Takeaway: Semantic types let the AI describe what data means, and the compiler uses that meaning to make all the visual design decisions automatically.
Given a compact Flint spec, the compiler automatically derives:
Input (what you provide):
├── Data fields + semantic types
├── Chart type (heatmap, bar, line, etc.)
└── Encodings (which field maps to x, y, color, size...)
Output (what compiler figures out):
├── Parsing rules (how to read dates, numbers)
├── Scales (linear? log? should it start at zero?)
├── Axis formatting (labels, tick marks, rotation)
├── Aggregations (sum? average? count?)
├── Color schemes (sequential? diverging? categorical?)
├── Layout (sizing, spacing, label room)
└── Full backend-native specification
Without Flint, to make a good heatmap you'd need to manually specify:
period field as a dateMonthYear values on the axisnewUsers data (which can be positive or negative)With Flint, you just say:
period → semantic type: date/periodnewUsers → semantic type: numeric (can be negative)The compiler handles everything else.
Key Takeaway: The compiler converts intent into implementation, freeing both humans and AI from error-prone low-level details.
Different chart libraries have completely different APIs:
Normally, switching libraries means rewriting your chart from scratch.
Because Flint is an intermediate representation (separate from any single library), the same compact spec can compile to any supported backend:
→ Vega-Lite spec
Flint spec → Compiler → ECharts spec
→ Chart.js spec
You choose the backend based on which capabilities best fit your needs, without changing your chart intent.
Key Takeaway: Write once, render anywhere. Flint decouples what you want from how a specific library implements it.
LLMs are good at:
LLMs struggle with:
LLM task with DirectVL (old way):
"Generate a complete Vega-Lite spec with exact scales,
axes, colors, spacing, formatting..."
→ Many opportunities for errors
LLM task with Flint (new way):
"Identify semantic types and chart intent"
→ Compiler handles the rest
Flint was compared against DirectVL (direct Vega-Lite generation) across three AI models:
| Model | Flint Score | DirectVL Score |
|---|---|---|
| GPT-4.1 | 16.27 | 15.91 |
| GPT-4o-mini | 16.16 | 15.60 |
| GPT-4.1 (alt) | 15.91 | 15.34 |
Flint consistently scored higher — the AI produces better charts when it only needs to express intent, not implementation.
Key Takeaway: Flint plays to AI's strengths (understanding meaning) and offloads AI's weaknesses (precise low-level details) to the compiler.
Model Context Protocol (MCP) is a standard way for AI agents to call external tools and services from within chat or coding environments.
User in chat: "Create a heatmap of monthly new users"
↓
AI Agent calls flint-chart-mcp server
↓
Server: creates spec → validates → compiles → renders
↓
Interactive chart appears in the chat interface
↓
User can inspect and refine
The server can:
Key Takeaway: The MCP server is the "plug" that connects Flint's capabilities directly into AI agent workflows, making chart creation a seamless part of conversation.
PROBLEM:
Good charts need many low-level decisions
AI agents struggle with low-level details
Short specs → bad charts; detailed specs → fragile code
SOLUTION (Flint):
Step 1: Human or AI writes COMPACT Flint spec
(chart type + field encodings + semantic types)
Step 2: Flint COMPILER infers all design decisions
(scales, colors, formatting, layout, parsing)
Step 3: Compiler outputs POLISHED spec
for chosen backend (Vega-Lite / ECharts / Chart.js)
Step 4: Beautiful chart renders
without anyone writing fragile low-level code
BONUS: MCP server makes this available
directly inside AI chat/coding environments
Flint separates chart intent (what you want to show) from chart implementation (how to technically render it), letting AI handle the former and the compiler handle the latter.