The Ken CHART ROOM · DATA JOURNALISM · PLOTLY

Data Journalism · Visualization Engineering

Inside The Ken's Chart Room

How we reverse-engineered 19 of The Ken's editorial data graphics into interactive Plotly charts — and distilled what we found into a reusable skill that encodes their visual storytelling language.

Data Journalism Plotly AI Skill 19 Charts · May 2026 · 12 min read

01 What is The Ken?

The Ken is one of India's most respected subscription business-journalism outlets. What sets it apart from most digital publications isn't just the depth of reporting — it's the quality of the accompanying data visualizations. Every chart is a piece of editorial journalism in its own right: opinionated, precise, and visually confident.

Their graphics don't just illustrate stories — they are the story in miniature. A Ken chart tells you what to think before you read a single paragraph. The headline is a claim. The subtitle is evidence. The chart is proof. This discipline, rare in Indian data journalism, is what made them such a compelling subject to reverse-engineer.

The Ken's editorial model: Title = claim. Subtitle = evidence in one sentence. Chart = visual proof. Together they tell the complete story before a reader opens the article.

02 The Brief

The goal was straightforward in concept but substantial in execution: take 19 published Ken charts — spanning stacked bars, slope charts, donuts, marimekko strips, Sankey diagrams, semicircle panels, and bespoke pill comparisons — and rebuild each one as an interactive Plotly HTML file. No Chrome. No static images. Pure Plotly, browser-native, hover-enabled.

Beyond the 19 replications, the real deliverable was a reusable skill: a principled understanding of what Ken does and why, encoded into a set of helpers and guidelines that can generate new Ken-style charts from scratch. The skill needed to capture not just how the charts were built but why they look the way they do.

Deliverables: 19 interactive HTML charts · A shared style helper (kenstyle.py) · An editorial design skill (SKILL.md) · A build runner that regenerates everything from a single command.

03 Five Chart Deep-Dives

Five charts that illustrate the breadth of techniques required — from the typographically dominant stacked bar to a full Sankey-plus-bars composite. In each pair: the original Ken graphic on the left, the interactive Plotly replication on the right.

01 Testing right — 100% stacked horizontal bar

Twelve Indian exam boards ranked by question-type composition. The typography carries the editorial weight: Playfair Display at 68px for "Testing right," an opinionated subtitle, bold segment labels inside each coloured band. The four segment colours — teal, cyan, magenta, gold — aren't random; they encode a taxonomy (Analysis vs. Application vs. Memory vs. Understanding) where cooler tones suggest rigour and warmer tones suggest rote-learning.

1
Testing right — How Indian boards weight question types
100% stacked bar
ORIGINALThe Ken, Apr '25
Testing right original
PLOTLYInteractive HTML
Key insight: The domain-band layout trick keeps the serif title, legend, and footer perfectly spaced without fighting Plotly's margin system. The bars sit in axis domain [0.04, 0.78]; the rest is clean paper space.

11 Turning the tables — Semicircle panels

Axis Bank's seven-year improvement in a single glance. Each FY lives in its own grey rounded panel — a visual "cell" that says this is one episode in a longer story. The pink half-disk (operating profit) and the two small bars (NNPA%, NIM%) coexist without a second y-axis. The semicircle's area signals weight; the bars encode precision. This is the chart that most clearly shows Ken's willingness to invent geometry when standard chart types aren't enough.

2
Turning the tables — Axis Bank's vitals FY18–FY24
semicircle panels
ORIGINALThe Ken, Sep '24
Turning the tables original
PLOTLYInteractive HTML
Key insight: Semicircles are built as SVG polygon paths in paper space (Plotly's shape types have no native arc). A helper function samples 48 points along θ ∈ [0, π] with separate x and y radii to compensate for the figure's aspect ratio — giving a truly circular dome rather than an ellipse. Open interactive chart ↗

05 Range of roadsters — Proportional pill comparison

Five EVs compared across price, range, acceleration, and top speed — but the comparison is built from badges, not bars. The price pill's width is proportional to price: Porsche spans the full width; Xiaomi sits at 24%. Three stat pills per car — lavender for range, pink for 0–100 time, green for top speed — sit at fixed columns regardless of value, because they are qualitative badges, not quantitative bars. The distinction between "scaled" and "fixed" pills is pure editorial judgment.

3
Range of roadsters — EV comparison by price and specs
pill comparison
ORIGINALThe Ken, Apr '24
Range of roadsters original
PLOTLYInteractive HTML
Key insight: Plotly rectangles have no border-radius. Every pill is an SVG path built from four straight segments connected by quadratic Bézier curves — Q x1,y1 x2,y2 — for the rounded corners. One helper function takes (x0, y0, x1, y1, r) and returns the complete path string. Open interactive chart ↗

16 Upstart face-off — 2×3 slope small multiples

Six Southeast Asian markets, one chart. Each country gets its own slope panel — Shopee (cyan) and Tiktok Shop (magenta) from 2022 to 2023 — with its own y-scale. The gain/loss verdict is embedded directly as coloured pills: red with a down-triangle for Shopee's loss, green with an up-triangle for Tiktok's gain. You read six mini-stories simultaneously and build a regional picture without a summary row.

4
Upstart face-off — Shopee vs. Tiktok Shop across SEA
2×3 small multiples
ORIGINALThe Ken, Sep '24
Upstart face-off original
PLOTLYInteractive HTML
Key insight: A 2×3 grid requires 6 axis-domain pairs. Plotly's layout keys follow the pattern xaxis, xaxis2, xaxis3… — not x, x2… (trace refs use the shorter form). Confusing these two conventions silently drops all traces — one of the trickiest bugs in the project. Open interactive chart ↗

19 Crucial anchor — Sankey + nested bars

The most structurally complex chart in the set: a Sankey of IPO proceeds flowing into anchor investors and then seven investor categories, combined with a horizontal nested-bar comparison of India's ten biggest IPOs — all in one figure. Two separate axis-domain regions share a canvas. The Sankey's colour-coded links tell the allocation story; the bars' visual nesting (total ⊃ anchor book ⊃ mutual funds) communicates subset relationships without arithmetic.

5
Crucial anchor — IPO proceeds and anchor investor allocation
Sankey + nested bars
ORIGINALThe Ken, Nov '25
Crucial anchor original
PLOTLYInteractive HTML
Key insight: go.Sankey and go.Bar can coexist in one figure if each occupies a different domain. The Sankey uses domain=dict(x=[0.10, 0.72], y=[0.40, 0.80]); the bars use a normal xaxis2/yaxis2 pair with domain=[0.05, 0.27]. They share canvas but never conflict. Open interactive chart ↗

04 How the Skill Was Built — the Nuances

Building these charts revealed a cluster of Plotly behaviours that aren't obvious from the documentation and can silently break a chart with no error message. Each one became a rule in the skill.

🐛
The category-axis ghost bug → always use numeric x

The single most damaging bug encountered: string categories on a bar axis silently disappear — and reverse their order — when another trace type (Scatter, Sankey, Pie) anchors the same figure. There's no warning. You see an empty chart. The fix is categorical but absolute: never use string x values for bars.

python — the fix
# ✗ String categories — bars vanish silently if any other trace is present fig.add_trace(go.Bar(x=["FY21","FY22","FY23"], y=values)) # ✓ Numeric x + manual tick labels — robust in all multi-trace figures fig.add_trace(go.Bar(x=[0, 1, 2], y=values)) fig.update_xaxes( tickmode="array", tickvals=[0, 1, 2], ticktext=["FY21","FY22","FY23"], range=[-0.6, 2.6] )

Grouped bars use fractional offsets around each integer: bar A at g−0.27, bar B at g, bar C at g+0.27.

📐
The domain-band layout trick — headers that stay put

Plotly's yref="paper" annotations are relative to the plot area, not the full figure. So pushing a title into a big top margin with margin=dict(t=150) means paper y=1.0 refers to the bottom of that margin — unpredictable and fragile. The solution is counterintuitive but reliable:

python — the layout pattern
# Keep margins tiny — paper space ≈ full figure fig.update_layout(margin=dict(l=10, r=10, t=10, b=10)) # Carve the data region with axis domain fig.update_layout(yaxis=dict(domain=[0.10, 0.78])) # Now paper bands are intuitive and stable: # y ≈ 0.95 → title (above plot) # y ≈ 0.81 → legend (between title and data) # y ∈ [0.10, 0.78] → data (the axis domain) # y ≈ 0.02 → footer (below data) fig.add_annotation(xref="paper", yref="paper", x=0.5, y=0.96, text="Giant MoMo", ...)
🔵
Pie centroids — placing labels at slice centres

Ken's pies display a large serif number inside each slice and a smaller name below it. Plotly's built-in textinfo uses a single font for all text — you can't have a 60px number and a 16px label in the same call. The solution is to suppress all pie text (textinfo="none") and compute slice centroid positions manually, then place two annotations per slice.

python — centroid math
def pie_centroids(values, domain_x, domain_y, fig_w, fig_h, rotation=0, rfrac=0.6): # Plotly: start at top (90° math), clockwise = decreasing angle total, cum = sum(values), 0.0 cx = (domain_x[0] + domain_x[1]) / 2 cy = (domain_y[0] + domain_y[1]) / 2 r_px = min((domain_x[1]-domain_x[0])*fig_w, (domain_y[1]-domain_y[0])*fig_h) / 2 for v in values: frac = v / total ang = 90 - rotation - 360 * (cum + frac/2) rad = math.radians(ang) x = cx + rfrac*r_px/fig_w * math.cos(rad) y = cy + rfrac*r_px/fig_h * math.sin(rad) yield x, y # paper-space label position cum += frac
🏹
Paper-space arrows — axref/ayref cannot be "paper"

Several Ken charts use call-out arrows anchored in paper space — the "+21%" arc in Coming of Age, the label leaders in Giant MoMo. The natural approach would be axref="paper" for the arrow tail. This is undocumented but the tail reference only supports "pixel" or axis names — not paper. The workaround: place the arrowhead at a paper coordinate and specify the tail as a pixel offset.

python — paper-space arrow
# ✗ Fails — axref="paper" is not supported fig.add_annotation(xref="paper", yref="paper", x=0.8, y=0.6, ax=0.7, ay=0.6, axref="paper") # ← BROKEN # ✓ Works — head in paper, tail as pixel offset fig.add_annotation(xref="paper", yref="paper", x=0.8, y=0.6, ax=-80, ay=0, # ← pixels from head axref="pixel", ayref="pixel", showarrow=True, arrowhead=2)
📏
Bubble sizing — diameter, not area

Conventionally, bubble charts scale marker area proportional to value (diameter ∝ √value) so that visual area encodes quantity accurately. Ken's bubbles are different: in Shooting for the Moon, the NPA% bubble for 7.2% is visibly about three times the diameter of the 2.3% bubble — consistent with diameter ∝ value, not area. The editorial choice prioritises visual impact over perceptual accuracy; the bubbles are signals, not precise encodings.

python — Ken bubble sizing
npa = [7.2, 2.9, 2.3] k = 21 # scale factor — tune visually # Ken style: diameter ∝ value (not √value) sizes = [v * k for v in npa] # [151, 61, 48] fig.add_trace(go.Scatter( x=gx, y=bubble_y, mode="markers", marker=dict(size=sizes, color="#7E72CE") ))

05 The Design Principles

Across 19 charts, ten recurring principles emerge. These aren't style guidelines — they're editorial decisions, each with a reason.

Title = claim, not label

"Giant MoMo" instead of "Vietnam e-wallet market share." The title takes a stance before the reader looks at a single bar.

Hero number, made massive

Every chart has a protagonist number rendered 2–4× larger than surrounding data labels. Size signals editorial priority, not data value.

Annotation as narration

"+21%" arc, "38%" Sankey label, red/green slope pills — annotations argue a point rather than naming a data element.

Color as character

In Fintech Frenzy, Fintech is gold among navy bars — not because it's fifth alphabetically, but because it's the subject of the article.

Shape as concept

Semicircle = weight. Proportional pill = runway. CAGR arc = trajectory. Ken invents geometry when standard chart types are insufficient.

Grey panels = mini-stories

Each FY in Turning the Tables is its own visual cell. Each country in Upstart Face-off is self-contained. Panels say: "one story per unit."

Two typographic eras

Newer pieces: Playfair Display + cream canvas (editorial). Older pieces: Archivo Black ALL CAPS + white (fact-forward). Both confident; neither decorative.

Footer as trust signal

Always: ◁ THE KEN · Graphic by [individual name], [date] · Source: [specific source]. Individual credit + specific attribution = accountability journalism applied to visual work.

The full editorial framework — all 11 principles with chart examples, chart-type selection guide, and Plotly implementation workflow — is documented in SKILL.md, available in the skill download below.

06 The Skill

Everything learned across the 19 replications is packaged as a reusable skill: a SKILL.md that documents the editorial principles with chart examples, a shared Python helper (kenstyle.py), two runnable example scripts, the original source images for reference, and all 19 chart scripts and rendered HTMLs.

the-ken-charts-skill
Editorial design principles + Plotly implementation helpers, grounded in 19 reverse-engineered Ken graphics. Includes SKILL.md, kenstyle.py, reference images, chart scripts, and rendered HTMLs.
SKILL.md kenstyle.py 19 chart scripts 19 rendered HTMLs 19 reference images build_all.py 2 example templates
↓ Download skill package (zip)

What's inside SKILL.md

The skill document is structured as a practitioner's guide, not a reference manual. It leads with the editorial principles (title = claim, hero number, annotation as narration, colour as character), illustrates each with specific chart examples from the 19, and then provides a 7-step implementation workflow — from "identify the editorial claim" through colour assignment, chart-type selection, and the Plotly implementation — ending with a one-page quick-reference of the 11 non-negotiables.

The technical helpers (canvas, header, footer, legend_chips, rounded_rect_path, semicircle_path, pie_centroids, paper_arrow, render) are documented with their rationale — not just their signatures. Knowing why paper_arrow uses a pixel tail offset matters as much as knowing how to call it.

07 All 19 Charts

Every chart in the set — click any to open the interactive Plotly version.

08 Conclusion

Reverse-engineering The Ken's charts turns out to be an education in editorial discipline as much as Plotly mechanics. The hardest part wasn't building semicircle paths or computing pie centroids — it was recognising that every visual choice is an argument. The title is a claim. The colour is a protagonist. The size is an emphasis. The annotation is a verdict.

The skill that emerged from this project isn't a style guide — it's a decision framework. Before any code is written: what is the editorial claim? Who is the hero number? What shape feels like th