Building a Malaysian Income Tax Calculator in Pure JavaScript
Calculating your Malaysian personal income tax doesn't have to be a mystery. In this tutorial, we'll build a working tax calculator using pure JavaScript and the 2024 LHDN (Inland Revenue Board) tax brackets.
By the end, you'll have a reusable function you can drop into any web project.
The 2024 Malaysian Income Tax Brackets
Malaysia uses a progressive tax system — you only pay the higher rate on income above each threshold. Here are the current rates for residents (chargeable income after deductions):
| Chargeable Income (RM) | Tax Rate |
|---|---|
| 0 – 5,000 | 0% |
| 5,001 – 20,000 | 1% |
| 20,001 – 35,000 | 3% |
| 35,001 – 50,000 | 6% |
| 50,001 – 70,000 | 11% |
| 70,001 – 100,000 | 19% |
| 100,001 – 400,000 | 25% |
| 400,001 – 600,000 | 26% |
| 600,001 – 2,000,000 | 28% |
| Above 2,000,000 | 30% |
Note: These rates apply to chargeable income — your gross income minus all eligible reliefs and deductions. For a full breakdown of what you can deduct, see this Malaysia tax filing guide.
Building the Calculator
Step 1: Define the Tax Brackets
const TAX_BRACKETS_2024 = [
{ min: 0, max: 5000, rate: 0.00, base: 0 },
{ min: 5001, max: 20000, rate: 0.01, base: 0 },
{ min: 20001, max: 35000, rate: 0.03, base: 150 },
{ min: 35001, max: 50000, rate: 0.06, base: 600 },
{ min: 50001, max: 70000, rate: 0.11, base: 1500 },
{ min: 70001, max: 100000, rate: 0.19, base: 3700 },
{ min: 100001, max: 400000, rate: 0.25, base: 9400 },
{ min: 400001, max: 600000, rate: 0.26, base: 84400 },
{ min: 600001, max: 2000000, rate: 0.28, base: 136400 },
{ min: 2000001, max: Infinity, rate: 0.30, base: 528400 },
];
Each bracket stores:
-
min/max— the income range -
rate— the marginal rate for income in this band -
base— tax already accumulated from all lower brackets (speeds up calculation)
Step 2: The Core Calculation Function
function calculateMalaysianTax(chargeableIncome) {
if (chargeableIncome <= 0) return 0;
const bracket = TAX_BRACKETS_2024.find(
(b) => chargeableIncome >= b.min && chargeableIncome <= b.max
);
if (!bracket) return 0;
const incomeInBracket = chargeableIncome - bracket.min + 1;
const taxInBracket = incomeInBracket * bracket.rate;
return bracket.base + taxInBracket;
}
How it works:
- Find which bracket the chargeable income falls into.
- Calculate tax only on the portion within that bracket.
- Add the pre-computed
basetax from all lower brackets.
Step 3: Add Effective Rate Helper
function getTaxSummary(chargeableIncome) {
const tax = calculateMalaysianTax(chargeableIncome);
const effectiveRate = chargeableIncome > 0
? ((tax / chargeableIncome) * 100).toFixed(2)
: 0;
return {
chargeableIncome,
tax: Math.round(tax * 100) / 100,
effectiveRate: parseFloat(effectiveRate),
};
}
Step 4: Test It
const examples = [30000, 60000, 120000, 500000];
examples.forEach((income) => {
const { tax, effectiveRate } = getTaxSummary(income);
console.log(`RM ${income.toLocaleString()} -> Tax: RM ${tax.toLocaleString()} (${effectiveRate}%)`);
});
Output:
RM 30,000 -> Tax: RM 300 (1.00%)
RM 60,000 -> Tax: RM 2,600 (4.33%)
RM 120,000 -> Tax: RM 14,400 (12.00%)
RM 500,000 -> Tax: RM 110,400 (22.08%)
Building a Simple HTML UI
Drop this into an HTML file for an instant tax calculator:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Malaysia Income Tax Calculator 2024</title>
<style>
body { font-family: sans-serif; max-width: 500px; margin: 40px auto; padding: 0 20px; }
input, button { width: 100%; padding: 10px; margin: 8px 0; font-size: 1rem; box-sizing: border-box; }
button { background: #1a73e8; color: white; border: none; cursor: pointer; border-radius: 4px; }
#result { background: #f0f4ff; padding: 16px; border-radius: 8px; margin-top: 16px; display: none; }
</style>
</head>
<body>
<h1>Malaysia Income Tax 2024</h1>
<p>Enter your <strong>chargeable income</strong> (after all reliefs):</p>
<input type="number" id="income" placeholder="e.g. 60000" min="0" />
<button onclick="calculate()">Calculate Tax</button>
<div id="result">
<p><strong>Chargeable Income:</strong> RM <span id="out-income"></span></p>
<p><strong>Estimated Tax:</strong> RM <span id="out-tax"></span></p>
<p><strong>Effective Rate:</strong> <span id="out-rate"></span>%</p>
</div>
<script>
const TAX_BRACKETS_2024 = [
{ min: 0, max: 5000, rate: 0.00, base: 0 },
{ min: 5001, max: 20000, rate: 0.01, base: 0 },
{ min: 20001, max: 35000, rate: 0.03, base: 150 },
{ min: 35001, max: 50000, rate: 0.06, base: 600 },
{ min: 50001, max: 70000, rate: 0.11, base: 1500 },
{ min: 70001, max: 100000, rate: 0.19, base: 3700 },
{ min: 100001, max: 400000, rate: 0.25, base: 9400 },
{ min: 400001, max: 600000, rate: 0.26, base: 84400 },
{ min: 600001, max: 2000000, rate: 0.28, base: 136400 },
{ min: 2000001, max: Infinity, rate: 0.30, base: 528400 },
];
function calculateMalaysianTax(income) {
if (income <= 0) return 0;
const b = TAX_BRACKETS_2024.find(b => income >= b.min && income <= b.max);
if (!b) return 0;
return b.base + (income - b.min + 1) * b.rate;
}
function calculate() {
const income = parseFloat(document.getElementById('income').value) || 0;
const tax = Math.round(calculateMalaysianTax(income) * 100) / 100;
const rate = income > 0 ? ((tax / income) * 100).toFixed(2) : 0;
document.getElementById('out-income').textContent = income.toLocaleString();
document.getElementById('out-tax').textContent = tax.toLocaleString();
document.getElementById('out-rate').textContent = rate;
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>
Important Caveats
This calculator estimates tax on chargeable income — not gross salary. Before using it, subtract your eligible reliefs (self, spouse, children, EPF contributions, lifestyle relief, etc.).
For a complete walkthrough of what you can deduct and how to submit your e-filing to LHDN, check out:
How to File Your Malaysia Tax Return (Step-by-Step)
To understand how to optimise your tax position and the difference between marginal and effective rates:
Malaysia Income Tax Guide for Individuals
Summary
In under 30 lines of JavaScript, you now have:
- Accurate 2024 LHDN tax brackets
- Progressive tax calculation with correct band logic
- Effective rate display
- A self-contained HTML widget
The calculateMalaysianTax() function is pure — no dependencies, no DOM — so it works equally well in Node.js, React, or a plain browser script.
Found this useful? Consider buying me a coffee — it keeps tutorials like this going!
Top comments (0)