ABOUT
Earth Estimator
Earth Estimator is a game about correctly estimating different statistics about different countries.
For more information about how to play, please have a read through the How To Play page.
This document explains why and how Earth Estimator was built.
WHY
Why build Earth Estimator?
The world is vast.
We, the affluent, statistically speaking, often live in parts of the world with clean air, access to clean water and electricity, high democracy indexes, and low corruption.
With high life satisfaction and higher life expectancy, it can be easy to forget that this is not the case everywhere in the world.
We forget about the parts of the world with higher poverty rates, lower life expectancies, less democracy, and fewer opportunities.
We sometimes even forget that not everyone in the developed world has it as easy as we do.
Sweden's homeless population: 27,377 in 2023
Sweden's homeless population: 33,250 in 2026
What does a 21.4% increase say?
Earth Estimator is fun, without a doubt, but Earth Estimator's aim is hopefully also to make you more aware of these differences in the world.
Statistics can feel distant when they are just numbers in a report.
Having to estimate them first, and then seeing how close or far away your assumption was, can make those numbers feel much more real.
Earth Estimator is a game.
But the numbers behind the game describe the real world.HOW
How Earth Estimator works
2.1
The Technology
Earth Estimator is primarily a browser-based JavaScript application.
The interface is built with HTML, CSS and JavaScript. Firebase is used for hosting, authentication and cloud data storage.
The statistics themselves are stored in a large normalized JSON dataset containing approximately 361,117 lines and 20,991 statistical observations.
Although the visible website is relatively simple, most of the technical work happens in four areas:
Collecting statistics from structured datasets, APIs and public statistical tables.
Converting different source formats into one predictable country and metric structure.
Building question ranges, scoring estimates and adapting numerical precision.
Turning thousands of country values into searchable tables and adaptive map colours.
2.2
Data Acquisition
The statistics were gathered from public statistical databases and datasets.
Two main techniques were involved: pulling structured information from APIs or downloadable datasets, and scraping public statistical tables when the information was not conveniently exposed through an API.
A simplified API-pulling technique looks like this:
[CODE PLACEHOLDER]
World Bank API request using:
- requests
- country/all
- indicator/SP.POP.TOTL
- JSON response
- country
- value
- year extraction
This type of request returns already structured records, meaning the main job is extracting the country, value and year before converting them into Earth Estimator's common format.
Other statistics required table-oriented extraction.
[CODE PLACEHOLDER]
Web scraping using:
- requests
- BeautifulSoup
- HTML table lookup
- <tr> rows
- <th> / <td> cells
- country
- value
- year extraction
The important distinction is that scraping returns presentation-oriented text, not necessarily clean statistical data.
Values therefore had to be converted from strings, footnote markers removed, missing values identified, years checked and country names normalized.
The original acquisition scripts are not part of the current website repository. These examples are representative reconstructions of the techniques used rather than verbatim copies of those scripts.
2.3
Normalizing Different Sources
One of the largest problems was that statistical sources do not agree on naming conventions.
One source might use:
United States
USA
United States of America
Another source may use:
Turkey
Türkiye
Earth Estimator therefore contains an explicit alias system.
[CODE PLACEHOLDER]
COUNTRY_ALIASES example:
"United States of America"
→ "United States"
→ "USA"
"Turkey"
→ "Türkiye"
The lookup code then tries every accepted alias until it finds a matching country record.
[CODE PLACEHOLDER]
findCountryData(
metricData,
country
)
1. Get aliases
2. Loop through aliases
3. Check metricData[alias]
4. Validate country record
5. Return matching data
6. Otherwise return null
This separates the display name from the source name.
Once cleaned, statistics are stored in one predictable structure.
{
{
"Population": {
"unit": "people",
"metric_explanation": "The total number of people living in the country at the stated time.",
"unit_explanation": "People are counted as individual residents.",
"Sweden": {
"value": 10605529,
"year": 2026,
"source": [
"https://ec.europa.eu/eurostat/api/dissemination/statistics/1.0/data/demo_gind?lang=en&geo=SE&indic_de=JAN&time=2026"
],
"audit": {
"status": "corrected",
"checked_on": "2026-09-11",
"data_kind": "reported_statistic",
"dataset": "Eurostat demo_gind; refreshed 2026-07-21T23:00:00+0200",
"indicator": "JAN",
"reference_period": "2026",
"year_basis": "observation_or_statistical_reference_year",
"notes": "Population stock at 1 January. Eurostat status:",
"latestness": "Latest eligible result in the checked structured/cached sources; years 2026 → 2025 → 2024 → 2023."
}
}
}
}
The browser can therefore treat population, GDP, life expectancy or another statistic using essentially the same JavaScript.
2.4
Loading the Dataset
The data map loads the complete dataset asynchronously.
[CODE PLACEHOLDER]
await fetch(
DATA_FILE,
{
cache: "no-cache"
}
);
gameData =
await response.json();
The program then discovers valid metrics dynamically instead of relying on one manually written question list.
[CODE PLACEHOLDER]
Object.entries(gameData)
→ filter valid metric objects
→ extract metric names
→ sort available questions
This means another properly formatted metric can be added to the JSON and become available to the program without rewriting the core rendering system.
2.5
Generating Questions
In Vanilla Mode, Earth Estimator first determines which countries contain enough usable statistics.
Every potential value is converted into a
number and checked using
Number.isFinite().
[CODE PLACEHOLDER]
const value =
Number(
countryData.value
);
if (
Number.isFinite(
value
)
) {
countryMetricCounts[
country
]++;
}
Only countries containing at least ten valid metrics become eligible for Vanilla Mode.
[CODE PLACEHOLDER]
if (
countryMetricCounts[
country
] >= 10
) {
eligibleCountries.push(
country
);
}
After the player chooses a country, the available metrics are randomized and ten distinct questions are selected.
[CODE PLACEHOLDER]
questionPool =
shuffleArray(
availableQuestions
).slice(
0,
10
);
Custom Mode works differently. It begins with the player's selected metric and country identifiers and searches the dataset for combinations containing a finite numerical value.
2.6
The Accuracy Calculation
Earth Estimator deliberately does not use a binary correct-or-incorrect scoring system.
The first calculation is the absolute difference between the player's estimate and the correct answer.
[CODE PLACEHOLDER]
const difference =
Math.abs(
playerAnswer -
correctValue
);
But an error of 10 cannot be judged without knowing the scale of the statistic.
The program therefore calculates the full available range:
[CODE PLACEHOLDER]
const valueRange =
currentMaximumValue -
currentMinimumValue;
The relative range error becomes:
where:
- d = |guess - correct|
- r = maximum - minimum
- e = normalized range error
[CODE PLACEHOLDER]
const rangeError =
difference /
valueRange;
Earth Estimator then applies its nonlinear accuracy curve.
[CODE PLACEHOLDER]
const remainingAccuracy =
Math.max(
0,
1 - (rangeError * 2)
);
answerAccuracy =
100 *
Math.pow(
remainingAccuracy,
2
);
Mathematically, this is:
Or, written using the original values:
|guess - correct|
A = 100 × max(0, 1 - 2 ─────────────────)²
maximum - minimum
The squaring is important. Without it, accuracy would fall linearly.
Squaring the remaining accuracy creates a stronger penalty as the estimate moves farther away from the real value.
The final game score is the arithmetic mean of all question accuracies.
[CODE PLACEHOLDER]
finalAccuracy =
totalAccuracy /
totalQuestions;
2.7
Slider Precision
The slider is generated from the statistics instead of using one arbitrary fixed range.
For each metric, the smallest and largest available observations become the slider boundaries.
[CODE PLACEHOLDER]
currentMinimumValue =
Math.min(
...values
);
currentMaximumValue =
Math.max(
...values
);
Earth Estimator also determines the smallest meaningful place value occurring in the source data.
Consider:
The program repeatedly divides the number by ten while it remains divisible by ten.
[CODE PLACEHOLDER]
while (
temporaryNumber % 10 === 0
) {
placeValue *= 10;
temporaryNumber /= 10;
}
The resulting place value becomes part of the slider's step calculation.
This lets the interface reflect the precision of the original statistics instead of inventing meaningless decimal places.
2.8
K, M and B Scaling
Large statistics are displayed using one common multiplier for the entire metric.
The program begins by finding the smallest non-zero observation.
[CODE PLACEHOLDER]
const smallestValue =
Math.min(
...nonZeroValues
);
It then chooses whether the entire metric should use normal numbers, thousands, millions or billions.
[CODE PLACEHOLDER]
if smallestValue >= 1,000,000,000
→ B
else if smallestValue >= 1,000,000
→ M
else if smallestValue >= 1,000
→ K
else
→ normal number
If the smallest relevant observation is 4,000,000, the divisor becomes 1,000,000.
The important part is that the divisor is selected once per metric, not once per country.
Every displayed country value therefore remains directly comparable with every other country.
2.9
The Global Data Map
The world visualization is built with jsVectorMap.
Earth Estimator first translates its authoritative country names into two-letter geographical codes understood by the map.
[CODE PLACEHOLDER]
const MAP_COUNTRY_CODES = {
"Sweden": "SE",
"Japan": "JP",
"Brazil": "BR",
"United States of America": "US"
};
The numeric observation can then be connected to the vector region representing that country.
Countries without numerical observations are initialized in grey.
[CODE PLACEHOLDER]
regionStyle: {
initial: {
fill:
"#c7c7c7",
stroke:
"#444444"
}
}
The more complicated part is calculating the colour of countries that do contain data.
Sending the raw values through a simple linear gradient caused extreme observations to dominate the map.
If most countries lie between 10 and 100 but one country has a value of 5,000, almost every normal country would otherwise receive practically the same colour.
Earth Estimator instead analyzes the distribution first.
2.10
Detecting Outliers
The map calculates the first and third quartiles of the selected statistic.
[CODE PLACEHOLDER]
const q1 =
calculateQuantile(
sortedValues,
0.25
);
const q3 =
calculateQuantile(
sortedValues,
0.75
);
The interquartile range is:
Earth Estimator then uses Tukey's interquartile-range rule:
[CODE PLACEHOLDER]
const iqr =
q3 -
q1;
const highOutlierBoundary =
q3 +
(
1.5 *
iqr
);
Only observations exceeding that boundary are treated as high outliers.
[CODE PLACEHOLDER]
if (
row.value >
highOutlierBoundary
) {
outlierRows.push(
row
);
}
2.11
Percentile-Based Map Colours
If no statistically meaningful outlier exists, countries are coloured according to their percentile positions instead of their raw values.
For n unique ordered values, the program calculates:
[CODE PLACEHOLDER]
const percentile =
index /
(
uniqueValues.length -
1
);
Colour therefore represents a country's position in the distribution, rather than simply its raw numerical distance from zero.
When real high outliers exist, normal values occupy approximately:
High outliers occupy:
The darkest part of the map can then extend all the way to black.
[CODE PLACEHOLDER]
scale: [
"#d9e5f5",
"#134287",
"#06152b",
"#000000"
]
As a result, an extreme country becomes nearly black only when it is actually extreme relative to the other countries.
2.12
Quantile Interpolation
Quartiles are not simply taken from one array position.
Earth Estimator first calculates a fractional position inside the ordered dataset.
[CODE PLACEHOLDER]
const position =
(
sortedValues.length -
1
) *
percentile;
If that position falls between two observations, linear interpolation is used.
Here, f represents the fractional distance between the lower and upper observations.
[CODE PLACEHOLDER]
return (
lowerValue +
(
(
upperValue -
lowerValue
) *
fraction
)
);
2.13
Search Ranking
Even the Data Map's question search has a small ranking algorithm.
Matching results receive a priority based on how closely they match the search.
[CODE PLACEHOLDER]
0 → exact match
1 → metric begins with search
2 → another word begins with search
3 → search appears somewhere else
For example, searching for:
can prioritize:
GDP
GDP per capita
Military spending % GDP
before weaker substring matches.
2.14
Local and Cloud Persistence
Earth Estimator maintains separate persistence systems for guests and signed-in players.
Game results and settings are stored in the browser using localStorage.
Game results and settings are stored using Firebase Authentication and Cloud Firestore.
Guest results can be stored using:
[CODE PLACEHOLDER]
gameHistory.push(
safeAccuracy
);
localStorage.setItem(
storageKey,
JSON.stringify(
gameHistory
)
);
Authenticated results instead become Firestore documents below the player's Firebase UID.
[CODE PLACEHOLDER]
users
/ {uid}
/ gameResults
/ {resultId}
.add(
resultData
)
A result document can contain:
{
accuracy: 84.7,
gameMode: "vanilla",
questionCount: 10,
country: "Sweden",
clientPlayedAt:
Date.now(),
playedAt:
serverTimestamp()
}
Using both client and server timestamps gives the application an immediate local time while Firestore also supplies an authoritative server-side timestamp.
The account storage path is tied to the authenticated Firebase uid, keeping one player's cloud results separate from another player's.
THE SYSTEM
What happens behind the game
The visible game is therefore only the final layer of a longer processing chain.
data acquisition
↓
cleaning
↓
country-name normalization
↓
common JSON schema
↓
question generation
↓
range + precision analysis
↓
accuracy mathematics
↓
percentile / IQR visualization
↓
local or Firestore persistence
Collecting, cleaning and restructuring the statistical data took the better part of a month.
Building the Earth Estimator platform itself took roughly three very intense weeks from the beginning of development to the first publishable version.
Development assistance
ChatGPT was used as a development tool for parts of the implementation, debugging and code revision, which considerably accelerated that process.EARTH ESTIMATOR
A simple game built on a lot of data.
Earth Estimator combines public statistics, browser-side JavaScript, statistical normalization, adaptive visualization and a custom scoring system into one geography game.
The idea is simple: make an estimate, see the real number, and gradually build a better intuition for the world.
You do not need to know every statistic. You just need to make the best estimate you can.
Start a Game