# Flint: Microsoft Research's Visualization Language for AI Agents

Asking an AI agent to generate a chart in Vega-Lite tends to produce one of two outcomes: a technically valid specification that looks mediocre, or a verbose JSON blob that renders nothing at all. The problem isn't that the model lacks chart knowledge; it's that Vega-Lite was designed for humans who can reason precisely about pixels, axis domains, and color gradients. LLMs can't.

[Flint](https://github.com/microsoft/flint-chart) is **a visualization intermediate language from Microsoft Research and Renmin University of China's IDEAS Lab that addresses this by separating what the chart means from how it should be rendered**. The AI describes meaning in a concise semantic specification; a deterministic compiler handles all the geometric and aesthetic decisions. The output is a correct, polished chart in Vega-Lite, ECharts, Chart.js, Plotly, or Excel-native format. This article covers the design, how to set it up with Claude Code, and where the current limitations are.

<iframe class="aspect-video h-auto" width="100%" height="315" src="https://www.youtube.com/embed/xFWhiWe91BE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


## The problem with direct chart generation

A typical Vega-Lite specification for a moderately complex chart can run to 100+ lines of JSON. It requires explicit values for axis domains, scale types, padding, color ramps, label formatting, and dozens of other parameters. These are precise, interdependent geometric decisions. A small error in an axis domain or a misplaced bracket breaks the entire spec.

LLMs are probabilistic token predictors. They're well-suited for semantic understanding and pattern recognition, but not for the kind of exact interdependent arithmetic that low-level chart specifications require. This is why AI chart generation success rates hover around 80%, which sounds reasonable until you realize that one in five charts failing is unusable for any real workflow.

The deeper issue is that chart generation is actually two jobs conflated into one:

**The meaning job:** Looking at a table with columns for `Month`, `Revenue`, and `Percentage Change` and understanding what each column represents. LLMs do this well.

**The math job:** Translating that understanding into exact axis scales, color palettes, tick formats, and padding. LLMs do this poorly.

Flint separates them.

## How Flint works

![A split-screen graphic elegantly showing "Meaning" on one side (representing the LLM's task) and "Math" on the other (representing the compiler's task).](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/01e66d95-7962-41fb-fb43-183fdd563f00/md2x =1280x720)

### Step 1: The LLM writes a short Flint spec

Instead of generating a full Vega-Lite specification, the model produces a concise Flint spec, typically around 10 lines of JSON. The spec has two main parts:

**`semantic_types`:** The model analyzes the data and assigns a semantic type to each relevant column. Flint ships with over 70 built-in types including `QUARTER`, `PRICE`, `RANK`, `PERCENTAGE_CHANGE`, `PROFIT`, `YEAR_MONTH`, and many others.

**`chart_spec`:** The high-level structure of the chart: `chartType` (e.g., Heatmap, Bar), `encodings` (which fields map to x, y, and color), and general `chartProperties` like desired `colorScheme`.

![An animation showing the structured extraction of semantic types from data, with a list of over 70 types like Quarter, Price, and Rank.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/af7cae03-e4e0-4f43-12c0-0f7bb16e9000/lg2x =1280x720)

There are no pixels, no axis domains, no color hex codes. The model's job ends when it has described what the chart means, not how to render it.

### Step 2: The Flint compiler generates the full spec

The compiler is a deterministic piece of software that reads the semantic description and makes all the low-level decisions based on data visualization best practices.

When it sees `PERCENTAGE_CHANGE`, it knows to use a diverging color palette centered on zero with red for negative and blue for positive values. When it sees a `PROFIT` column, it calculates a sensible axis scale with clean tick marks. When it sees `Heatmap`, it generates the correct `rect` marks and encodings.

![A diagram illustrating the Flint compiler taking a single input and branching out to produce compiled specs for Vega-Lite, ECharts, and Chart.js.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/5df81ecd-a2c4-4039-e6fc-bca75c37a800/public =1280x720)

The output is a complete, correct specification for whichever rendering backend you're using.

## Setting up Flint with Claude Code

Flint is a TypeScript library. The MCP server (`flint-chart-mcp`) makes it available to any agent that supports MCP, including Claude Code.

In your project directory, add Flint as an MCP tool:

```command
claude mcp add flint --scope project -- npx -y flint-chart-mcp
```

This creates a configuration file in your project that makes the `render_chart` and `create_chart_view` tools available whenever you're working in that directory.

Place your data file (in this example, `signup_data.csv` with columns for `month`, `game`, and `new_users`) in the same directory. Then prompt the agent:

```text
[output]
Call render_chart with a heatmap of new_users by month and game on the csv I have. I need the actual PNG to appear in the chat. Also show the short Flint spec.
```

The agent identifies the file, generates a ~10-line Flint spec with appropriate semantic types and encodings, calls `render_chart`, and returns a PNG image directly in the chat.

![The beautiful and clear heatmap of new user signups, generated as a PNG file by the Flint process.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/479c92c2-a107-446e-abbb-2da5dff91000/md2x =1280x720)

To make the chart interactive, follow up with:

```text
[output]
Now call create_chart_view on that same heatmap.
```

This opens an interactive panel where you can change the color scheme, adjust sorting, and inspect individual data points by hovering. The agent does the initial work; you handle the fine-tuning.

![The interactive chart view panel, demonstrating the ability to change the heatmap's color scheme on the fly from the default blue to a vibrant "Plasma" theme.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/9119ee42-1121-4bb4-92fb-e1e3e24a6500/lg1x =1280x720)

## The semantic layer in action

The real power of the semantic layer is visible when you change a single type annotation. Suppose `new_users` is actually a month-over-month percentage change rather than an absolute count.

With semantic type `Quantity`, the compiler produces a sequential color scale (light to dark blue) starting from the data minimum, which is correct for an absolute count.

Change the semantic type to `PercentageChange` and the compiler makes a completely different set of decisions: it switches to a diverging color scheme (red-white-blue), centers the midpoint on zero, and formats labels and tooltips with a `%` symbol. The entire visual logic of the chart changes from one word in the spec.

![A clear flow diagram showing the Flint Spec on the left, being compiled into a much more verbose Vega-Lite spec in the middle, which then renders the final visualization on the right.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/ad128f90-c10c-44f7-6130-54a9e6629c00/orig =1280x720)

This is the core value of the semantic layer: the model doesn't need to know how to implement a diverging color scale. It only needs to know that the data is a percentage change. The compiler handles the rest.

## Flint vs. Vega-Lite

Flint doesn't replace Vega-Lite. It compiles to it. The relationship is similar to a high-level programming language compiling to assembly: Vega-Lite is powerful and explicit but verbose and brittle for machine generation. Flint is concise, focused on intent, and relies on the compiler to handle the details that LLMs should never have been trusted to improvise.

## Current limitations

Flint is a Microsoft Research project published in July 2026 and is still in early development.

**The chart type coverage is solid for common visualizations but doesn't yet include maps, 3D charts, network graphs, or complex chart layering**. Accessibility features are on the roadmap but not yet implemented. The core library is TypeScript/JavaScript only, though a Python port (`flint-py`) is in preview in the repository and a PyPI package is expected. **The compiler also doesn't yet handle all edge cases around high-cardinality data or highly customized layouts that require manual parameter overrides**.

That said, the project is already being used internally at Microsoft in their [Data Formulator](https://github.com/microsoft/data-formulator) analytics tool, and the pattern it introduces, using a semantic intermediate representation as the contract between LLM and deterministic compiler, is a generalizable approach that's likely to show up across other AI tooling categories as well.

The [GitHub repository](https://github.com/microsoft/flint-chart) includes the core `flint-chart` library, the `flint-chart-mcp` server, an interactive gallery, and documentation for both the agent workflow and direct library usage. The [Microsoft Research blog post](https://www.microsoft.com/en-us/research/blog/flint-a-visualization-language-for-the-ai-era/) covers the design rationale in more depth.