A geography-based educational adventure game where young explorers unlock countries, discover fascinating facts about animals, foods, cultures, and world history.
TL;DR 🎮 Browser game for ages 7-12 • Open source (GPLv3) • 5 countries to explore • Intellectually stimulating without oversimplification • Translation-ready for global education
🎮 Play Now at worldspinner.org
World Spinner is an interactive browser game designed for children aged 7-12. Players spin a virtual globe to reveal mystery countries, solve progressive clues, and collect discovery cards filled with age-appropriate educational content.
The game uses intellectually stimulating language without oversimplification, encouraging curiosity and deeper learning through exploration.
- 🎡 Spin the Globe: Random country selection with smooth animations
- 🕵️ Progressive Clues: Three hints per country (animal, food, flag)
- ✍️ Guess & Learn: Case-insensitive validation with country name aliases
- 🎉 Discovery Cards: Educational facts about each unlocked country
- 📱 Responsive Design: Works seamlessly on phones, tablets, and laptops
- 🎨 Polished UI: Teal color theme with Framer Motion animations
- 🌐 Internationalization: Full English and Spanish language support with easy-to-use language switcher
- 🔄 Language Persistence: Your language preference is saved between sessions
- 👥 Translation-Ready: Community-friendly structure for adding new languages
- 🎯 Smart Card Removal (NEW): Each country appears only once per session—no repetitive discoveries!
- 📊 Progress Tracking (NEW): See how many countries you've discovered with a live counter
- 🏆 Game Completion (NEW): Celebrate with an animated congratulations screen when you've found all countries
- 🔄 Manual Reset (NEW): Start fresh anytime with the reset button—no need to finish the game first
- Frontend: React (JavaScript)
- Build Tool: Vite (fast dev server, instant HMR)
- Styling: Tailwind CSS
- Animations: Framer Motion
- Testing: Jest (≥80% coverage)
- Linting: ESLint with Airbnb config
- Formatting: Prettier (120 char line length)
See Stack.md for complete technical specifications.
WorldSpinner/
├── app/ # React application
│ ├── src/ # Source code
│ ├── public/ # Static assets
│ └── package.json # Dependencies
├── Ideas.md # Feature ideas and future enhancements
├── Backlog.md # User stories and development tasks
├── Stack.md # Technology stack decisions
├── CHANGELOG.md # Version history
└── README.md # This file
- Node.js (v22 or higher)
- npm or yarn
# Clone the repository
git clone https://github.com/zosorock/worldspinner.git
cd worldspinner/app
# Install dependencies
npm install
# Start development server
npm startThe game will open at http://localhost:5173
cd app
npm test # Run tests in watch mode
npm run test:coverage # Generate coverage reportcd app
npm run build # Outputs to app/dist/We welcome contributions, especially translations! World Spinner is designed to be easily translatable so children around the world can learn geography in their native language.
Want to help kids learn in their language? We'd love your help! Follow these beginner-friendly steps:
- Navigate to the
app/src/locales/directory - Copy
en.jsonand rename it using the ISO 639-1 language code for your language:- French:
fr.json - German:
de.json - Portuguese:
pt.json - Japanese:
ja.json - And so on...
- French:
Open your new file and translate only the values (the text in quotes after the :) — keep all the keys in English!
Good example:
{
"buttons": {
"spinGlobe": "🎡 Faire tourner le globe"
}
}Bad example (don't translate the keys):
{
"boutons": {
"faireTournerLeGlobe": "🎡 Faire tourner le globe"
}
}Important guidelines:
- Keep the structure: Your file must have the exact same nested structure as
en.json(max 2 levels deep) - Keep the keys: Never translate the keys like
buttons,spinGlobe,feedback, etc. - Keep the emojis: Button emojis (🎡, 🔍, ✍️) are visual cues that work in any language
- Keep the placeholders: Text like
{country}or{current}/{total}are replaced by the game — don't translate these - Age-appropriate language: World Spinner is for kids ages 7-12, so keep translations simple and friendly
- Add a comment: Include a
_commentfield at the top describing your translation (seeen.jsonfor an example)
Your translation file is ready! Now you need to register it in two places:
3a. Import your translation in TranslationContext
Edit app/src/contexts/TranslationContext.jsx:
-
Import your translation file (around line 12):
import frTranslations from '../locales/fr.json';
-
Add your language to
VALID_LANGUAGESarray (around line 15):const VALID_LANGUAGES = ['en', 'es', 'fr'];
-
Add your translations to the
translationsmap (around line 18):const translations = { en: enTranslations, es: esTranslations, fr: frTranslations, };
3b. Add a language button to the Switcher
Edit app/src/components/LanguageSwitcher.jsx:
- Find the existing language buttons (around lines 50-83)
- Copy one of the existing button blocks (EN or ES)
- Update the button with your language code, flag emoji, and aria-label
Example for French:
<motion.button
type="button"
onClick={() => handleLanguageClick('fr')}
className={getButtonClasses(language === 'fr')}
aria-label="Passer au français"
aria-pressed={language === 'fr'}
whileTap={{ scale: 0.95 }}
>
<span className="flex items-center gap-1.5">
<span role="img" aria-label="French flag">
🇫🇷
</span>
<span>FR</span>
</span>
</motion.button>First time? Install dependencies:
cd app
npm installRun automated tests to verify your translation file structure:
npm test -- locales.test.jsThe tests will verify:
- Your JSON file is valid
- All keys match the English version exactly
- The structure is max 2 levels deep
- You included a
_commentfield
Common test failures:
- "keys do not match" → You added/removed a translation key. Copy the structure from
en.jsonagain. - "structure is not max 2 levels deep" → You nested categories too deep. Keep it to 2 levels max.
- "does not have documentation comment" → Add a
_commentfield at the top of your file.
Manual testing:
npm startThen click your language button and explore the app to make sure everything looks good!
Option A: Using Git (recommended for developers)
- Fork this repository on GitHub
- Create a new branch for your translation:
git checkout -b add-french-translation
- Commit your changes:
git add app/src/locales/fr.json app/src/contexts/TranslationContext.jsx app/src/components/LanguageSwitcher.jsx git commit -m "Add French translation" - Push to your fork:
git push origin add-french-translation
- Open a Pull Request on GitHub
Option B: Using GitHub's web interface (easier for non-developers)
- Fork this repository using the "Fork" button on GitHub
- Navigate to the files you modified in your fork
- Click "Add file" → "Upload files" to upload your new
fr.json - Click the pencil icon (Edit) to modify
TranslationContext.jsxandLanguageSwitcher.jsx - Create a Pull Request from your fork's main page
In your Pull Request, include:
- A clear title: "Add [Language] translation"
- A description mentioning you followed the translation guide
- Any questions or notes about translation choices
We'll review your contribution and may ask questions about specific translation choices. Don't worry — we're here to help!
- Main translations: app/src/locales/en.json (template to copy)
- Guidelines: app/src/locales/_README.json (technical reference)
- Translation registration: app/src/contexts/TranslationContext.jsx (import and register your language)
- Language switcher: app/src/components/LanguageSwitcher.jsx (add your button here)
- Translation tests: app/src/locales/locales.test.js (validates structure)
Not sure about something? Open an issue on GitHub with the "translation" label and we'll be happy to help!
- Check Backlog.md for current user stories
- Review Ideas.md for feature ideas
- Follow the tech stack in Stack.md
- Ensure tests pass and coverage stays ≥80%
- Follow Airbnb JavaScript Style Guide
- 🦫 Capytan the Capybara: Friendly game host character
- 🌍 More Countries: Expand beyond the initial 30 countries
- 🏆 Progression System: Continental badges and explorer levels
- 🎨 Enhanced Visuals: Country images and illustrations
- 🔊 Audio Support: Pronunciation guides and sound effects
See Ideas.md for all planned features and enhancement ideas.
This game was entirely inspired by my eldest son, Liam, boundless love for learning, with contributions from my creative director, Nolan (my youngest) and advise, supervision, and production assistance from my lovely and multi-talented wife, Carolina. You three are the best things that ever happened to me. 🥰
Built with curiosity, exploration, and a love of learning in mind.
World Spinner is dual-licensed:
- Source Code: Licensed under GPLv3 - Free to use, modify, and share. If you distribute your version, you must share the source code under the same terms.
- Game Assets: Licensed under CC BY-NC-SA 4.0 - Free to remix and share for non-commercial purposes with attribution.
See LICENSE.md and ASSETS_LICENSE.md for complete details.
