DEV Community

Cover image for Why I Open-Sourced the Python Calculation Engine Behind GetBirthChart

Why I Open-Sourced the Python Calculation Engine Behind GetBirthChart

I’ve been building GetBirthChart for a while, and one thing kept bothering me.

From the outside, a birth chart app can look like one big black box.

You enter a date, time and place. A chart appears. Then an interpretation appears.

But those are actually two very different problems.

One is calculation.

The other is interpretation.

I wanted that boundary to be obvious, not just in the UI, but in the architecture itself.

So I open-sourced the Python calculation engine behind GetBirthChart.

The part AI shouldn’t be doing

GetBirthChart uses AI for interpretation.

But I don’t want an LLM deciding where the Moon was when someone was born.

That belongs in deterministic code.

The architecture is roughly:

birth data
   ↓
Python calculation engine
   ↓
structured chart data
   ↓
application / interpretation layer
   ↓
AI + UI
Enter fullscreen mode Exit fullscreen mode

The Python layer calculates the chart.

The AI layer receives the result and explains it.

That distinction sounds simple, but it changes a lot.

If the interpretation says someone has a Scorpio Moon, there should be a calculated Moon placement behind that statement.

The model shouldn’t “know” the placement from training data or try to reconstruct it itself.

What the engine handles

The core is written in Python and uses Swiss Ephemeris for the astronomical calculations.

It handles things such as:

  • planetary positions
  • zodiac placements
  • houses
  • Ascendant and Midheaven
  • aspects and orbs
  • retrograde state
  • normalized chart output

A basic calculation looks roughly like this:

from gbc_astro import AstrologyEngine

engine = AstrologyEngine()

chart = engine.natal(
    local_datetime="1992-11-03T14:35:00",
    timezone="Asia/Ho_Chi_Minh",
    latitude=21.0285,
    longitude=105.8542,
)

print(chart.bodies["sun"].sign)
print(chart.bodies["moon"].sign)
Enter fullscreen mode Exit fullscreen mode

The interesting part is not really that you can turn 221 degrees of longitude into Scorpio.

That part is easy.

The difficult parts tend to be around the edges:

  • timezone conversion
  • daylight saving transitions
  • unknown birth times
  • house-system limits
  • circular angle math
  • aspect tolerances
  • making every assumption explicit

Those are also the things I wanted people to be able to inspect.

Why make it public?

Partly because I think calculation software should be inspectable.

If someone sees a chart result and asks:

Why did this calculator give me this Ascendant?

“Trust the website” isn’t a very useful answer.

With the engine public, the actual calculation path can be inspected and tested.

But there was another benefit I didn’t expect as much.

Open-sourcing it forced me to clean up the architecture.

Private code can survive with assumptions that only the original developer understands.

Public code is much less forgiving.

I had to make things like calculation profiles, errors, warnings and output structures clearer.

That made the production system better too.

Unknown birth time is a good example

This was one of the design choices I cared about from the beginning.

If someone says:

I don’t know what time I was born.

the easiest implementation is to pick a default.

Noon is common.

Midnight is common too.

That gives you a nice complete chart.

It also creates a Rising sign and houses from a birth time the user never gave you.

I didn’t want to do that.

The engine has an explicit unknown-time mode instead.

chart = engine.natal(
    local_datetime="1992-11-03",
    timezone="Asia/Ho_Chi_Minh",
    latitude=21.0285,
    longitude=105.8542,
    unknown_time=True,
)
Enter fullscreen mode Exit fullscreen mode

Time-dependent parts of the chart are then omitted rather than silently fabricated.

That means the resulting object may be less complete.

I think that’s a feature.

I’d rather return:

Ascendant: unavailable
Houses: unavailable
Enter fullscreen mode Exit fullscreen mode

than return confident-looking information based on a fake noon birth time.

Open source isn’t a claim that astrology is science

This is another boundary I want to keep clear.

Publishing the code makes the software inspectable.

It doesn’t prove astrology.

The engine answers a technical question:

Given these inputs and these calculation rules, what chart does the software produce?

Interpretation is a different layer.

GetBirthChart treats that layer as a reflective framework rather than a scientific prediction system.

I think the product becomes more honest when those two things are separated.

The bigger lesson for me

The part I like most about this architecture has very little to do with astrology.

It’s this:

deterministic domain logic
        ↓
structured evidence
        ↓
AI interpretation
Enter fullscreen mode Exit fullscreen mode

I’m using the same idea more and more in products that involve LLMs.

If something has a deterministic answer, calculate it outside the model.

Give the model good evidence.

Then let the model do what it’s actually good at: explaining, comparing and synthesizing.

The Python engine is now public here:

GitHub: https://github.com/getbirthchart-com/gbc-astro-engine

And the hosted implementation is what powers:

GetBirthChart: https://getbirthchart.com/

I’ll probably write more about the less obvious parts of building it, especially timezone handling, unknown birth times and aspect calculations. Those turned out to be much more interesting engineering problems than I expected.

Top comments (0)