If you've ever tried to build software around Bazi (八字, also called Four Pillars of Destiny), you already know the pain. The terminology is scattered across a hundred sources, most of it Chinese-only. And every chart UI you can find is welded to one specific calculation engine — want to render a chart from a different engine, or show English labels, and you're rewriting the whole view layer.
So I built bazi-kit, an MIT-licensed monorepo that fixes the two layers underneath the calculation:
-
bazi-terms— a zero-dependency, bilingual (中文 / English) terminology database. -
bazi-chart— a React component that renders a complete chart from whatever data you feed it.
The one design rule that ties them together: bazi-kit never calculates anything. It doesn't compute pillars, luck cycles, or element scores. It takes chart data you already have and turns it into clean, bilingual, structured output or UI. Bring your own engine.
Why split data & presentation away from calculation?
Almost every Bazi library couples three very different jobs into one blob: (1) the astronomical/calendar math, (2) the domain vocabulary, and (3) the rendering. That coupling is why the ecosystem feels stuck — you can't swap an engine without rewriting the UI, and you can't reuse the terminology without dragging in someone's calculation code.
bazi-kit deliberately open-sources only (2) and (3). The vocabulary and the presentation are generic infrastructure — every Bazi app needs them, and none of them are a secret. The interpretation logic (the part that actually reads a chart) stays in the product. That boundary is what makes the library safe to reuse: it's a presentation layer, not an oracle.
bazi-terms — a bilingual terminology database, zero dependencies
Every concept a chart uses is structured, translated data you can drop into an app, a blog glossary, or an i18n layer.
| Category | Export | Size |
|---|---|---|
| Ten Heavenly Stems 天干 | STEMS |
10 |
| Twelve Earthly Branches 地支 | BRANCHES |
12 |
| Five Elements 五行 (+ cycles) |
ELEMENTS, ELEMENT_CYCLES
|
5 |
| Ten Gods 十神 (+ Day Master) | TEN_GODS |
11 |
| Twelve Growth Stages 十二长生 | GROWTH_STAGES |
12 |
| Shen Sha 神煞 | SHEN_SHA |
38 |
| Sixty Jiazi Na Yin 六十甲子纳音 | NA_YIN |
30 melodies / 60 pairs |
| Interactions 刑冲合会 | INTERACTION_TYPES |
8 |
| General vocabulary 通用术语 | GENERAL_TERMS |
40+ |
Each term carries key, zh, en, an optional pinyin, and an English definition you can paste straight into a tooltip.
The workhorse is translate(), which never throws — unknown input is returned unchanged, so it's safe to pipe raw engine output straight through it:
import { translate, t, naYinOf, interactionFromEnum } from 'bazi-terms';
t('dayMaster'); // 'Day Master' — translate a term KEY
translate('元男'); // 'Day Master' — aliases resolve (元男/元女/日元)
translate('甲子'); // 'Sea Gold' — Na Yin by sexagenary pair
translate('metal', 'zh'); // '金'
translate('something-else'); // 'something-else' — unknown passes through, no throw
naYinOf('甲子'); // { key: 'seaGold', en: 'Sea Gold', pairs: ['甲子','乙丑'] }
interactionFromEnum('CLASH');// { key: 'liuChong', en: 'Clash', ... }
That "never throws" property matters more than it sounds. Most Bazi engines emit Chinese values for ten gods, Na Yin, growth stages and Shen Sha — even when their field names are English. Where engines differ (some use 元男 for the Day Master, some emit interaction enums like CLASH), bazi-terms normalizes them through aliases and dedicated resolvers, so one zh → key mapping serves them all.
bazi-chart — render any chart, in Chinese, English, or both
Feed <BaziChart /> your chart data. It auto-detects the input shape, normalizes it, and renders — no calculation, no configuration ceremony.
import { BaziChart } from 'bazi-chart';
// `data` = raw output from your engine; the shape is auto-detected.
export default function Reading({ engineOutput }) {
return <BaziChart data={engineOutput} lang="both" theme="light" />;
}
import { BaziChart, type NormalizedChart } from 'bazi-chart';
// …or a chart object you built / normalized yourself.
export default ({ chart }: { chart: NormalizedChart }) => (
<BaziChart chart={chart} lang="en" theme="dark" />
);
It renders the full picture: the Four Pillars (main star, stem/branch glyphs colour-coded by element, hidden stems and their ten gods, Na Yin, growth stage, void branches, Shen Sha), Five-Element score bars, the interactions panel (刑冲合会), the Luck Pillar (大运) timeline, and auxiliary palaces (命宫/身宫/胎元/胎息) when the source provides them. Sections a source doesn't emit are simply omitted.
Styling is inline-only — no CSS import, no build step, safe for SSR and any meta-framework (Next.js, Remix, Astro). Element colours follow the traditional Five-Element palette (wood=green, fire=red, earth=brown, metal=gold, water=blue), tuned for contrast in both light and dark themes.
The NormalizedChart contract
The reason bazi-chart can stay engine-agnostic is a single intermediate shape — NormalizedChart (meta, solarTime, dayMaster, pillars, fiveElements, daYun, interactions, extras). Adapters only reshape raw output and keep the original Chinese values; everything derivable (element, polarity, colour, translation) is resolved at render time via bazi-terms.
Two common community shapes are auto-detected — a top-level 八字 object, and a pillars map keyed by year/month/day/hour — and you can drive the adapters yourself when you want the normalized data without the component (for a custom UI, a table export, or an API response):
import { normalize, detectEngine } from 'bazi-chart';
detectEngine(engineOutput); // 'baziObject' | 'pillarMap' | 'normalized' | 'unknown'
const chart = normalize(engineOutput); // -> NormalizedChart
// chart.pillars, chart.dayMaster, chart.daYun, chart.interactions, ...
normalize() throws a clear error for unrecognized input instead of failing silently.
Install
npm install bazi-terms # zero dependencies
npm install bazi-chart # React component (react >= 17 peer; bazi-terms installed automatically)
Both ship ESM + CJS builds with full TypeScript declarations.
What's next
bazi-kit is the open-source presentation layer underneath AskingMing, an AI Bazi-reading product I'm building — but the terminology and the chart component are meant to be useful to anyone building in this space, independent of that.
It's early (v0.1.0), so feedback is genuinely welcome:
- Missing terminology, a wrong translation, or a Na Yin/Shen Sha edge case? File an issue.
- Using a chart shape that isn't auto-detected? The adapter layer is small and PR-able.
- Want a non-React renderer (Vue, Svelte, or plain HTML)? The
NormalizedChartcontract is designed to make that a separate, thin package.
GitHub: https://github.com/favkit/bazi-kit · npm: bazi-terms / bazi-chart
Top comments (0)