Build log: a free BaZi chart calculator and one-time PDF reports on Cloudflare Workers
I've been building PillarChart — live under the product name BaZi Chart — a small site that turns a birth date, time and place into a BaZi (Four Pillars) chart, shows every step of the calculation, and offers optional on
I've been building PillarChart — live under the product name BaZi Chart — a small site that turns a birth date, time and place into a BaZi (Four Pillars) chart, shows every step of the calculation, and offers optional one-time PDF reports.
BaZi isn't rare on the English web. What I wanted was different from the "drop a birthday, get a destiny score" pattern: a calculator whose rules you can audit, content pages that cite classical sources instead of inventing personality claims, and a monetization path that doesn't need a subscription. The funnel shape is familiar — free chart, then a paid written report, the same shape that works for other niche calendar/astrology calculators with sample PDFs — but the visual language and the content depth are not copied from anyone else's layout. I cared more about looking like a careful calendar tool than like a generic "AI oracle."
This post is the engineering and editorial log: the engine, the receipt, the Workers stack, the content wedge, and how the paid reports sit on top of deterministic chart facts.
The stack
Everything runs on Cloudflare:
-
Next.js 16 deployed to Workers via
@opennextjs/cloudflare - D1 for accounts, orders and report jobs
- R2 for finished PDF reports
-
Queues for report generation (
max_batch_size = 1,max_retries = 3) - Auth.js (next-auth) for Google sign-in when someone wants a written report
- pdf-lib to assemble the report PDFs on the Worker
The chart engine itself is custom TypeScript under src/lib/bazi/. It never calls a network API. Solar-term instants for 1900–2100 are a committed lookup table generated offline with Astronomy Engine (SearchSunLongitude). Independent calendar libraries (lunar-javascript, tyme4ts, sxtwl) appear only in tests, as cross-check oracles — they are not the production engine.
Why a receipt matters more than a reading
Most BaZi disagreements online aren't mystical. They're convention disagreements:
- Does the year change at Li Chun (Sun at ecliptic longitude 315°) or at Chinese New Year?
- Does the day change at 23:00 (start of the Zi hour) or at midnight?
- Is the hour taken from clock time, or corrected to true solar time at the birthplace (longitude + equation of time, daylight saving removed)?
- Was daylight saving stripped out before the hour pillar was set?
Our defaults are published on /methodology: Li Chun year, solar-term months, true solar time on when you give a city, day change at 23:00 (switchable to midnight). Every chart prints an engine version — currently bazi-lichun-jieqi-tst-v1 — and a receipt: UTC instant, DST minutes removed, longitude and equation-of-time corrections, the solar terms before and after the birth, and the resulting true solar time.
If you don't know your birth time, you get three pillars and no hour. The form never fills in noon.
How I check the engine (published numbers)
I don't want to claim "accurate" without saying against what. The methodology page lists the checks that ship with the site:
- Solar terms: all 4,824 terms from 1900–2100 compared with sxtwl, tyme4ts and lunar-javascript. Largest difference: 73 seconds. Sample published Li Chun instants match to the minute.
- Four pillars: 10,000 random births plus 2,500 near a boundary, against lunar-javascript (midnight day change) and tyme4ts (23:00 day change), with no differences; another 1,200 births within three minutes of a solar term also match.
- Luck pillars: 5,000 random births vs lunar-javascript's minute-precise method — same direction and pillars; start offset within one day.
- Other calculators: reproduced the published outputs of seven calculators in the open Jade Almanac conventions dataset (CC0) once each tool's conventions were aligned.
- Manual spot check: 20 charts vs bazi-calculator.com; 18 match on all pillars. The two that differ are births 14 and 18 minutes after Li Chun — border cases that site itself flags.
Those numbers live on the public methodology page, not in a private spreadsheet. If a future engine revision changes a rule, the version string on the chart changes with it.
The free layer (no account)
Three calculators are free and need no sign-up:
- Full BaZi chart on the homepage — pillars, hidden stems, Ten Gods labels, weighted five-element count, Day Master strength, luck pillars when gender is given, plus the receipt.
-
/day-master-calculator— just the Day Master stem, for people who only searched that question. -
/compatibility— two charts side by side: Day Master element relation and stem combinations, Ten Gods both ways, day-branch harmony/clash/same/none, and five-element complement vs doubling. No score, no percentage match, no "compatible / not" verdict. It also doesn't do Chinese zodiac (year-animal) compatibility — different method.
Nothing from those free flows is saved.
Thick content instead of thin SEO glue
A calculator alone feels thin. I wrote a first wave of English explainers that sit next to the engine and quote only passages I could check against an online edition (Wikisource / Chinese Text Project), with our own translations labelled as such:
| Live page | What it's for |
|---|---|
/methodology |
Every calculation rule, sources, school disagreements, verification |
/classical-sources |
Which books we cite (Di Tian Sui, Yuanhai Ziping, Sanming Tonghui, Ziping Zhenquan, …) and for what |
/li-chun-bazi-year-start |
Li Chun vs Chinese New Year, with worked boundary charts |
/five-elements |
Generating/controlling cycles, seasonal strength, how we count |
/hidden-stems |
Twelve-branch table from the Yuanhai Ziping verse; our weights called out as ours |
/weak-day-master · /strong-day-master
|
What the labels mean; thresholds (42% / 58%) are our method |
/day-master/jia-wood |
Pilot Day Master page (甲); more stems follow the same template |
/about |
Editorial team (no invented master lineage), how AI is and isn't used |
The about page is deliberate: charts are never computed by AI; optional short written readings for signed-in users are disclosed separately; articles are drafted with AI assistance and classical quotes are checked against linked editions. We write "the Di Tian Sui describes…" — not "you will…".
One-time reports (not a subscription)
After a free chart, the result page offers written PDF reports. Prices are listed on /pricing:
| SKU | Price | What's in it |
|---|---|---|
| Essential Natal Report | $19 | 10 chapters · listed as 11–14 pages |
| Complete Natal + 10-Year Luck Report (featured) | $29 | Everything in Essential + luck-pillar chapters and a decision filter · 14 chapters · listed as 15–23 pages |
| Compatibility Report | $19 | Two charts · 8 chapters · listed as 9–13 pages |
| 2027 Annual Forecast | $24 | Year pillar vs natal + current luck · month by month · listed as 11–13 pages |
Anyone with a Google account can buy a report after signing in with Google — the free chart still needs no account. Delivery is a PDF (sample downloads are already public). Refunds: delivery failures in full; wrong payment or wrong birth details reviewed within 7 days; delivered personalized reports are not normally refunded — same wording as the pricing page.
Each chapter is built the same way: chart evidence (facts the engine already computed) → a reading → one question to reflect on. There is no overall life score. The only percentage in a natal report is the Day Master strength share from the published point method.
Samples you can read without paying
-
/sample-report— Complete report for one fictional person (real engine chart: 15 June 1990, 14:30, Shanghai → 庚午 / 壬午 / 辛亥 / 乙未; Xin Day Master; weak at 38% on our method). Sample PDF: 15 pages. The page states clearly that Essential has 10 of Complete's 14 chapters, so Essential buyers don't expect luck-pillar chapters. -
/sample-compatibility-report— Maya and Leo, two fictional people, two real charts (Chicago / Manchester). Sample PDF: 9 pages. Relations shown are ones the engine actually computes (e.g. Wood produces Fire; Mao–You clash) — still no match score.
Turning a chart into a PDF on a Worker
The paid path is intentionally boring:
- You calculate a free chart (still no account).
- On the result page you pick Essential or Complete (or start from the compatibility calculator for the pair report).
- Sign in with Google (any Google account) to check out that SKU; the order stores the chart query, not a free-form essay prompt.
- A queue consumer builds chapter facts from the same engine functions the free page uses, fills the template (and may polish prose with a model when enabled — the sample pages show the template wording), assembles a PDF with pdf-lib, stores it in R2, and emails a time-limited download link.
That order of operations is the whole point of the product bet: if the pillars were wrong, prettier prose would make it worse. The expensive mistake in this category is letting a model invent a clash or a Ten God the code never computed. Keeping the engine authoritative and the PDF downstream is how I avoid that.
The 2027 annual SKU is the same pipeline with an extra year-pillar overlay: natal chart + current luck pillar + month-by-month walk of 2027. It is listed on pricing now so people who care about the coming Li Chun boundary can see it early.
Design notes (without pretending to be a temple)
I wanted the UI to feel like paper, ink and vermilion calendar work — because BaZi is a calendar system — not a generic "mystic AI" gradient. Stems, branches and solar terms belong to the calculation. Motifs that belong to other Chinese traditions (luopan compass, Hetu/Luoshu, bagua logos) stay out unless they're clearly labelled as something else. The homepage already walks year → month → hour the way the engine does: Li Chun, the twelve jie, true solar time.
Lessons so far
- Publish the conventions, or you'll spend forever explaining "why does site X disagree?" The receipt and the methodology page are the product, not a footer link.
- Keep the LLM away from the pillars. Deterministic code owns the chart; any prose sits on top of facts the code already produced. The sample natal page even says buyers get the template text when no writing model is used.
- Cross-check libraries are oracles, not dependencies. Shipping tyme4ts (or any single library) as "the" engine would hide which conventions you chose. Generating a solar-term table with Astronomy Engine and owning the rest of the rules made verification reports possible.
- Compatibility without a score is a feature. People arrive from zodiac-compatibility habits; saying what we don't compute (year animals, match %) sets better expectations than a fake 87%.
- One Day Master page as a pilot beats ten thin stubs. Jia Wood is live; the other nine stems reuse the same outline once the depth and citation style are locked.
- Same funnel shape, different surface. Free calculator → sample PDF → one-time report is a proven indie pattern. The differentiation is auditability and content thickness, not a novel checkout flow.
If you want to see the receipt on a real birth (yours or a fictional one), try the free chart on PillarChart. No account for the calculator. After you sign in with Google, you can buy a one-time PDF report from the result page or pricing. If you read Chinese metaphysics content for fun, I'd especially like feedback on where the methodology page is still unclear, or which Day Master page you'd want next after Jia Wood.
Questions about the OpenNext/Workers setup, the solar-term table pipeline or the report chapter structure are welcome in the comments.
BaZi is a traditional Chinese cultural practice. PillarChart / BaZi Chart is for reflection, education and entertainment — not medical, legal, financial or psychological advice. Different schools may read the same chart differently.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.



