Building a tiny choose-your-own-adventure with vanilla JavaScript
I wanted to build a small adventure game that felt like stepping into a story: a moonlit forest, a few meaningful choices, and more than one way for the journey to end. Mosslight is a browser game where you explore the
I wanted to build a small adventure game that felt like stepping into a story: a moonlit forest, a few meaningful choices, and more than one way for the journey to end.
Mosslight is a browser game where you explore the woods, collect a few items, and search for the moon bell. Play it here. Itβs built with plain HTML, CSS, and JavaScriptβno framework, build step, or package dependencies.
Put the story in data
Each scene is an object with its own text and choices. A choice can move the story forward, add an item, or change the ending:
{
title: "A river with no end",
choices: [
{
label: "Offer a moonleaf to the water",
requires: "moonleaf",
consume: "moonleaf",
next: "tower",
flag: "river_blessing"
},
{
label: "Cross on the slippery stones",
next: "tower",
health: -1
}
]
}
Keeping the choices with the scene made the story easy to expand and helped keep the game logic out of the page markup.
Keep state small
The game tracks the current scene, warmth, pocket items, and a few story flags. When a player chooses something, the game updates that state and renders the next scene:
function choose(choice) {
if (choice.item) state.inventory = addUnique(state.inventory, choice.item);
if (choice.consume) {
state.inventory = state.inventory.filter(
(item) => item !== choice.consume
);
}
if (choice.flag) state.flags = addUnique(state.flags, choice.flag);
if (choice.health) {
state.health = Math.max(0, state.health + choice.health);
}
state.steps += 1;
if (state.health === 0) {
state.ending = "lost";
} else if (choice.ending) {
state.ending = choice.ending;
} else {
state.scene = choice.next;
}
render();
}
Story text is rendered as text rather than injected HTML. After a choice, focus moves to the new scene heading so keyboard players can follow the transition.
Make the atmosphere part of the interface
The forest illustration is made from CSS shapes and gradients, so the game needs no image assets. The layout adapts for smaller screens, and the firefly animation respects reduced-motion preferences.
The result is a short game with different routes, item-dependent choices, and endings shaped by what you did along the way. I tested a complete route to the moon-bell ending, the out-of-warmth ending, and the restart flow in the browser.
Thereβs room to grow the story, but keeping the scene data and game state simple made it possible to get a playable version running quickly. Try Mosslight.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.