A tiny, zero-dependency percentage calculator for JavaScript covering 7 common calculation modes: percentage of a total, percentage change, discount, increase, share of a total, tax/VAT extraction, and reverse percentage. No build step, no external libraries.
Live demo → · Full-featured version on roversia.it →
Percentage math is simple in isolation but easy to get wrong in practice — sequential discounts don't add up linearly, VAT extraction isn't the same formula as VAT addition, and "reverse percentage" trips people up constantly. This library implements the 7 most common real-world cases as small, tested, pure functions.
- 📊 7 calculation modes in one package
- 📦 Zero dependencies, no build step
- 🌐 Works as ESM, CommonJS, or plain
<script>tag - 🧪 Fully tested against real-world example values
- ✅ Explicit errors on invalid input (e.g. division by zero) instead of silent
NaN
Copy src/percentage-calculator.js into your project, or clone this repo. No npm package yet — open an issue if you'd find one useful.
import { percentageOf, applyDiscount } from './percentage-calculator.js';
percentageOf(20, 150); // 30 — "what is 20% of 150?"
applyDiscount(100, 25); // { finalPrice: 75, saved: 25 }<script src="percentage-calculator.js"></script>
<script>
const result = window.PercentageCalculator.percentageOf(20, 150);
</script>What is percentage% of total? Returns a number.
percentageOf(20, 150); // 30Percentage change between two values. Positive = increase, negative = decrease. Throws if oldValue is 0.
percentageChange(100, 120); // 20
percentageChange(120, 100); // -16.666...Returns { finalPrice, saved }.
applyDiscount(100, 25); // { finalPrice: 75, saved: 25 }Note: sequential discounts don't add up. A 10% discount followed by a further 5% is not a flat 15% — apply each step to the previous result:
const step1 = applyDiscount(100, 10); // { finalPrice: 90, ... }
const step2 = applyDiscount(step1.finalPrice, 5); // { finalPrice: 85.5, ... }Returns { finalPrice, added }.
applyIncrease(100, 10); // { finalPrice: 110, added: 10 }What percentage is value of total? Throws if total is 0.
shareOf(30, 150); // 20
shareOf(75, 1400); // 5.357142857142857Extract a tax/VAT amount from a gross total that already includes it. Returns { net, taxAmount }. Throws if taxPercent <= -100.
extractTax(122, 22); // { net: 100, taxAmount: 22 } — 22% VAT already included in 122Work backwards to the original value from a value that already had a percentage applied. Use a negative percentage for an already-applied discount, positive for an increase. Throws if appliedPercent <= -100.
reversePercentage(80, -20); // 100 — a price discounted 20% down to 80 was originally 100
reversePercentage(110, 10); // 100 — a price increased 10% up to 110 was originally 100Why do sequential discounts not just add up? Because each discount applies to the already-reduced price, not the original. 10% + 5% on €100 gives €90 → €85.50, not €85. The real combined discount is 14.5%, not 15%.
What's the difference between "percentage change" and "percentage points"?
Going from 20% to 25% is a change of 5 percentage points, but a percentage change of 25% (since (25−20)/20 × 100 = 25). This library's percentageChange() always returns the percentage-change figure, not percentage points.
Does this handle currency formatting?
No — it returns plain numbers. Format them however fits your locale (e.g. Intl.NumberFormat).
Why no npm package? Keeping this dependency-free and copy-pasteable on purpose. Open an issue if there's demand for one.
This is one of 39+ free browser-based tools I maintain at roversia.it. The full-featured version includes a live UI with all 7 modes, formula display, and copy-to-clipboard.
MIT © Andrea Roversi