`tax-engine`: The Tax Calculator That Chooses a Regime for You
A small Flask app turns Indian tax slabs, deductions, and rebates into a decision engine that compares both regimes, explains the difference, and nudges you toward the better outcome.
- `tax-engine` is interesting because it computes both Indian tax regimes and then decides which one is better, turning tax logic into a recommendation system.
- The repo’s strongest move is hybrid intelligence: it combines slabs, rebate checks, deduction caps, cess, and plain-language tips into one flow.
- Its frontend adds product value with animated results, INR formatting, and what-if ranges, so the experience feels more like guidance than a raw calculator.
- The project is useful precisely because it stays small and legible, which also makes its limits easy to see.
The easiest mistake is to read `tax-engine` as a calculator. It is closer to a decision engine. It takes a salary, runs both Indian tax regimes, applies the legal quirks, then tells you which path looks better.
A Rule-engine based tax calculation engine.
The Indian tax choice this repo is actually solving
The user problem is not hard math. It is the old regime versus new regime decision, where the answer depends on deductions, rebates, and the shape of income. A spreadsheet can solve it, but only after the user already knows what to compare.
This repo makes the choice first-class. It turns the tax question into a branch: compute both paths, compare the outputs, then recommend one.
Why the engine computes both regimes every time
| Step | Old regime | New regime |
|---|---|---|
| Input | Gross salary plus deductions | Gross salary |
| Main logic | Apply capped deductions before tax | Apply slab iteration directly |
| Special rule | Standard deduction and Section 80C caps | Section 87A rebate when eligible |
| Final adjustment | Add 4% cess | Add 4% cess |
| Output | Final liability and savings tips | Final liability and savings tips |
That structure matters because it changes the product from a static calculator into a comparator. The app is not asking, “What is your regime?” It is asking, “What is the best regime for this profile?”
Under the hood: slabs, rebate, cess, and deduction caps
The Python code is straightforward in a good way. The new regime uses slab iteration over a list of brackets, then checks the Section 87A rebate threshold, then adds the 4% health and education cess. The old regime works differently: it reduces taxable income with legal caps such as `min(deductions.get("sec80c", 0), 150000)` before tax is computed.
def calculate_old_regime(income, deductions):
taxable_income = income
taxable_income -= min(deductions.get("sec80c", 0), 150000)
taxable_income -= min(deductions.get("sec80d", 0), 25000)
taxable_income = max(taxable_income, 0)
tax = compute_old_regime_tax(taxable_income)
cess = tax * 0.04
return tax + cess
def calculate_new_regime(income):
tax = 0
slabs = [(300000, 0.0), (600000, 0.05), (900000, 0.10), (1200000, 0.15)]
for upper, rate in slabs:
# slab math
pass
if income <= 700000:
tax = 0
cess = tax * 0.04
return tax + cess
That is the right level of complexity. You can see every rule, but the code does not bury the reader in legal prose. It expresses policy as a sequence of small decisions.
The frontend does more than display a number
The JavaScript is doing product work. `animateCount` turns the result into a live reveal. `formatINR` uses Indian numbering conventions correctly. `renderRangeTable` goes one step further and shows how the liability shifts if income moves up or down.
That makes the app feel less like a form and more like a tool. The green and red cues do a lot of heavy lifting too, because they make the comparison legible at a glance.
function formatINR(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR'
}).format(value);
}
function animateCount(el, target) {
const start = 0;
const duration = 800;
const steps = 30;
const increment = (target - start) / steps;
let current = start;
const timer = setInterval(() => {
current += increment;
el.textContent = formatINR(Math.round(current));
if (current >= target) clearInterval(timer);
}, duration / steps);
}
The robo-advisor layer
The most product-like function in the repo is `get_tax_saving_tips`. It does not just say how much tax you owe. It looks at unused deduction room and turns that into plain-language advice about where savings might still exist.
That is the difference between information and guidance. A calculator returns a number. A small advisor tells you what the number means and what to do next.
What this repo is not
| Category | `tax-engine` | Commercial tax SaaS |
|---|---|---|
| Scope | Single-country salary tax decisioning | Broad jurisdictional compliance |
| Maintenance | Manually encoded rules | Continuously updated tax content |
| Deployment | Small Flask app | Managed platform or enterprise service |
| Best fit | Learning, prototypes, internal tools | Production compliance at scale |
| Trade-off | Clarity and control | Coverage and operational depth |
This is not Avalara. It is not Vertex. It is not a generalized rule platform like Drools. It is a narrow, understandable system that captures one decision space well enough to be useful.
Where this fits in the broader tax-tech landscape
The comparison with commercial tax SaaS is really a comparison between two models. In one, tax content is a service you buy. In the other, tax rules are code you own. `tax-engine` sits firmly in the second camp.
That gives it a different kind of value. You get control, transparency, and easy reasoning. You do not get a massive content operation, legal coverage, or enterprise-grade guardrails.
The trade-off that defines the project
The repo’s identity is defined by a single trade-off: simplicity versus completeness. It is compact enough to understand in one sitting, yet opinionated enough to recommend a regime and surface savings tips.
That is why it works as an open-source artifact. It shows how code can help users choose between two legal realities without hiding the logic behind a black box.