Why I Built a Free, Private Global Salary Calculator That Runs 100% in Your Browser
Negotiating an international job offer is one of the most nerve-wracking experiences in a software engineer's career. You get a shiny number on a screen, but you quickly realize that $120,000 in Lisbon feels radically different from $120,000 in San Francisco or London. Most tools out there force you to hand over your personal email, sign up for a newsletter, or sell your detailed salary data to headhunters just to see what your take-home pay would actually be.
I got tired of hidden paywalls, creepy data-harvesting trackers, and clunky spreadsheets that broke every time a tax bracket changed. So, I decided to build my own solution: a free, open, and completely private Global Salary Calculator that normalizes income across eight countries, compares job offers side-by-side, and executes every single tax calculation 100% client-side.
The Problem Everyone Ignores
When software engineers evaluate job offers across borders, they almost always fall into the gross salary trap. We look at the headline number on the offer letter and mentally convert it using current forex rates. This naive math completely ignores local tax brackets, social security contributions, healthcare deductions, and mandatory pension funds.
What makes this worse is the privacy nightmare built into modern web tools. Most salary comparison sites exist solely to collect candidate data. When you input your current income, target location, and years of experience into a traditional calculator, that data is immediately piped into customer relationship management platforms and sold to recruitment agencies. You become the product before you even know if the job pays enough to cover rent.
The technical community often ignores how complex local tax legislation really is. A single mistake in calculating effective tax rates versus marginal tax rates can lead to a $10,000 variance in your estimated annual budget. When you are relocating your life or switching to a global remote contract, a surprise discrepancy like that can derail your financial stability.
// A simple client-side tax engine supporting multiple tax brackets
export class TaxEngine {
constructor(brackets, standardDeductions) {
this.brackets = brackets; // Array of { threshold, rate }
this.standardDeductions = standardDeductions;
}
calculateNetIncome(grossSalary) {
const taxableIncome = Math.max(0, grossSalary - this.standardDeductions);
let totalTax = 0;
let previousThreshold = 0;
for (const bracket of this.brackets) {
if (taxableIncome > previousThreshold) {
const taxableAmountInBracket = Math.min(
taxableIncome - previousThreshold,
bracket.threshold - previousThreshold
);
totalTax += taxableAmountInBracket * bracket.rate;
previousThreshold = bracket.threshold;
} else {
break;
}
}
return taxableIncome - totalTax;
}
}
This simple, deterministic class demonstrates how marginal tax calculation logic can run instantly inside the user's browser without calling an external backend.
What Actually Works
To solve the double burden of mathematical complexity and privacy intrusion, the architecture needs to be entirely client-side. Web browsers are incredibly fast execution environments, capable of running thousands of tax policy simulations in milliseconds using plain JavaScript or WebAssembly. By keeping the calculations inside the browser memory, we eliminate network latency and eliminate the need for server infrastructure.
Before jumping into implementation, we must establish a unified data structure that normalizes different national tax rules into a predictable engine format. Each country has standard deductions, tiered income brackets, and mandatory social insurance contributions. By decoupling the calculation core from country-specific policy JSON objects, we build an extensible architecture where adding a new country requires zero changes to the UI engine.
Building anticipation around a zero-backend tool comes down to performance and trust. Users instantly notice when an input slider updates three side-by-side offer columns in under a millisecond without triggering network spinners.
// Decoupled tax parameters for international calculation engines
export const countryTaxProfiles = {
US: {
currency: 'USD',
standardDeduction: 13850,
socialSecurityRate: 0.062,
medicareRate: 0.0145,
brackets: [
{ threshold: 11000, rate: 0.10 },
{ threshold: 44725, rate: 0.12 },
{ threshold: 95375, rate: 0.22 },
{ threshold: 182100, rate: 0.24 },
{ threshold: Infinity, rate: 0.32 }
]
},
UK: {
currency: 'GBP',
standardDeduction: 12570,
nationalInsuranceRate: 0.08,
brackets: [
{ threshold: 50270, rate: 0.20 },
{ threshold: 125140, rate: 0.40 },
{ threshold: Infinity, rate: 0.45 }
]
}
};
This configuration structure maps national tax policies into flat data structures that our client-side engine can iterate through effortlessly.
Step-by-Step: Let's Build It Together
We will build the entire application pipeline using modern JavaScript modular patterns, focusing on state isolation, fast calculations, and dynamic offer comparisons.
Step 1: Normalizing Currencies and Exchange Rates
We need a local exchange rate provider that fetches rates once, caches them in IndexedDB or localStorage, and performs all cross-currency conversions locally without sending user input anywhere.
// Local Currency Normalizer with offline-first caching
export class CurrencyConverter {
constructor(baseCurrency = 'USD') {
this.baseCurrency = baseCurrency;
this.rates = {};
}
async initializeRates() {
const cached = localStorage.getItem('forex_rates');
if (cached) {
const { timestamp, rates } = JSON.parse(cached);
// Cache valid for 24 hours
if (Date.now() - timestamp < 86400000) {
this.rates = rates;
return;
}
}
// Fetch fallback static bundle or public API
const response = await fetch('https://open.er-api.com/v6/latest/USD');
const data = await response.json();
this.rates = data.rates;
localStorage.setItem('forex_rates', JSON.stringify({
timestamp: Date.now(),
rates: data.rates
}));
}
convert(amount, fromCurrency, toCurrency) {
if (fromCurrency === toCurrency) return amount;
const amountInBase = amount / this.rates[fromCurrency];
return amountInBase * this.rates[toCurrency];
}
}
This module guarantees that once exchange rates are downloaded, all salary conversions happen entirely offline in browser memory.
Step 2: The Multi-Offer Comparison State Manager
Next, we need a lightweight state manager that tracks multiple job offers, applies local tax rules, and normalizes all net outputs into a single base currency for comparison.
// Offer Comparison State Management Pipeline
export class OfferManager {
constructor(currencyConverter) {
this.converter = currencyConverter;
this.offers = [];
}
addOffer(offer) {
// offer: { id, title, grossSalary, countryCode, currency }
this.offers.push(offer);
}
calculateNormalizedOffers(targetCurrency = 'USD') {
return this.offers.map(offer => {
const profile = countryTaxProfiles[offer.countryCode];
const engine = new TaxEngine(profile.brackets, profile.standardDeduction);
const netLocal = engine.calculateNetIncome(offer.grossSalary);
const netNormalized = this.converter.convert(netLocal, offer.currency, targetCurrency);
return {
...offer,
netLocal,
netNormalized,
targetCurrency,
effectiveTaxRate: ((offer.grossSalary - netLocal) / offer.grossSalary) * 100
};
});
}
}
This step aggregates individual salary profiles, runs local tax deductions, and converts every offer into a common currency for side-by-side analysis.
The Mistakes That Will Burn You
When developers attempt to build client-side financial tools, they often fall into predictable trapdoors that compromise accuracy or user experience.
- Mistake 1: Conflating marginal tax rates with effective tax rates. If you apply a 30% top-bracket tax rate to a developer's entire salary, your net pay calculations will be wildly inaccurate.
- Mistake 2: Relying on real-time API calls for every user interaction. Sending a network request every time a user moves a slider causes UI lag, breaks offline capabilities, and leaks sensitive financial inputs over the wire.
- Mistake 3: Hardcoding currency conversion rates. Forex markets fluctuate constantly, and hardcoding static exchange rates will distort offer comparisons between regions like the EU, UK, and US over time.
Production Checklist
Before pushing a financial tool like this live, run through this checklist to ensure complete data integrity and user trust.
- Do this: Validate tax rules against official government documentation for every supported country annually.
- Do this: Store all application state in browser memory or LocalStorage so no personal payload ever touches a remote database.
- Do this: Add explicit visual indicators showing users that calculations are running 100% locally on their device.
- Never do this: Inject third-party tracking scripts or analytics pixels that log keypresses or field input values.
- Never do this: Silently round down currency conversions without accounting for micro-decimals during calculations.
Key Takeaways
- Client-side architecture provides complete privacy and zero latency for complex financial calculations.
- Decoupling calculation engines from configuration profiles makes adding new country tax rules simple.
- Real-world international compensation decisions require comparing net take-home pay, not gross contract numbers.
- Offline-first design principles ensure the tool remains usable regardless of network stability or backend availability.
Engr. Hamza | AI & MLOps Engineer | Building autonomous systems at the edge of possibility

Top comments (0)