Files
lemonade/README.md
T
jcoffey-dev 7fcb863771 Remaster: cel-shaded skin, a crowd that tracks trade, and a funk band
Two skins over one game. 1979 is the machine as it was; the remaster is
the same rules in cel-shaded vector art with a modern palette. The
simulation is untouched - only the paint differs - and the toggle sits
under the set.

The street is now driven by the game. Before prices are in, the number
of people walking on comes from the forecast; afterwards it comes from
the glasses that actually sold, so the report shows the crowd you
earned. A heat wave has them fanning themselves; a downpour leaves one
figure hurrying past under an umbrella.

Selling no longer snaps straight to the books. The stand trades for a
few seconds first, tally climbing and till filling, which is where that
crowd animation pays off. Skippable.

The synth grew a kit (pitched-sine kick, snare with body, hats), a
sweeping resonant lowpass for the bass, chords and a shuffle - enough
for a funk band. The chiptunes stay for the 1979 skin.

Boot is now a cassette load, which doubles as the gesture browsers
require before audio will play.

Fixes found on the way: the picture scaled about its centre while flex
auto-margins collapsed to zero on overflow, which pushed the report's
buttons off the bottom; and the validation line moved the button when
it appeared, so it is now reserved and faded. Sibling tabs in one
browser also keep their score tables in step.
2026-09-08 15:59:52 -07:00

144 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lemonade Stand
A browser recreation of the Atari 8-bit BASIC classic — the one you typed in,
ran on a TV, and lost two dollars to on a cloudy day.
It ships in two skins over one game. **1979** is the machine as it was: Atari
blue, block letters, scanlines, chiptune. **Remaster** is the same rules with
cel-shaded art, a crowd that turns up in proportion to trade, and a funk band.
The rules never differ between them — only the paint. Switch any time with the
button under the set.
React + TypeScript + Vite, and not one binary asset: every pixel is SVG drawn
in code and every note is generated by a small synth on the Web Audio API.
## Playing
```
npm install
npm run dev
```
It loads from cassette first, because that is how it arrived. Pressing PLAY on
the recorder is also what unlocks the audio — browsers want a gesture before
they will make a sound, and that happens to be exactly the gesture the game
already wanted.
One to four players share the keyboard, hot-seat style. Each morning you see
the weather, then decide three things: how many glasses to make, how many
15-cent signs to put up, and what to charge. Then the town votes with its
pocket money.
- Lemonade costs 2 cents a glass on days 12, 4 cents through day 7, and
5 cents after that.
- Everything you make is made *today*. Unsold glasses are poured away.
- A hot, dry day brings a bigger crowd that will pay more. A heat wave is
better still.
- A cloudy day can turn into a thunderstorm, and a thunderstorm ruins every
glass you made.
- Street crews close the road now and then, and they tend to stay for a
second day.
- Signs work, but with sharp diminishing returns — the fourth one barely
earns its 15 cents.
Once the prices are in, the stand trades for a few seconds before the books
open: the crowd arrives at the rate you actually sold, the tally climbs, and
the till fills. It can be skipped, but the day is worth watching.
You are broke when you cannot afford a single glass. Otherwise the summer runs
as long as you like; **RETIRE** closes the books and shows the standings.
## High scores
Retiring writes every player's closing balance to a top-ten table kept in
`localStorage`, reachable from the title screen and from the standings. Runs
from the summer you just finished are picked out in yellow. Ties break on the
shorter season, then on the earlier date.
There is no way to clear the table. A stand leaves the list only by being
pushed off the bottom by a better one, so a good summer stands until somebody
beats it.
The table is per-browser, not per-device, and it is the one piece of state the
game keeps between visits. Anything already in storage can be edited by hand,
so every field is validated on the way back in and malformed rows are dropped
rather than trusted. If storage is unavailable — a private window, or a browser
set to block site data — the game plays normally and the table simply stays
empty.
## How it is put together
```
src/
game/ pure simulation - no React, no DOM
constants.ts costs, weather profiles, the demand curve
engine.ts rolls each day and settles the takings
reducer.ts the day/turn state machine
rng.ts seeded mulberry32, so a run can be replayed
highscores.ts the persisted table, with validation on load
audio/
synth.ts pulse voices, a drum kit, filters, look-ahead sequencer
tunes.ts the 1979 chiptunes
funk.ts the remaster's band
skin.ts which of the two skins is on, and where it is kept
components/
PixelScene the 1979 artwork, in chunky rectangles
CelScene the remaster's artwork, and the people on the street
Scene picks between them, so no screen needs to care
```
The simulation is deliberately kept out of React: `engine.ts` takes a player, a
decision and a day and hands back a `DayResult`. That is what makes it possible
to run a few hundred seasons in a script and check that a careful player grows
their two dollars while a reckless one goes broke about seven times in ten.
### The demand curve
The original BASIC listing is not reproduced line for line. `constants.ts`
holds a reconstruction tuned to behave the way the game plays: cheap lemonade
sells out, sales fall to nothing once the price stops being a bargain, heat
lifts both the crowd and the price people will tolerate, and signs help a lot
at first and hardly at all later.
### The television
An Atari 800 fed a 4:3 television, and a television has no scrollbar. The tube
is locked to 4:3 and everything inside it is sized in container units, so the
picture stays in proportion at any size. If a page ever did come out taller
than the tube, `Fit` scales it down rather than clipping or scrolling it.
### The people on the street
How many figures walk on is driven by trade, not decoration. Before you have
priced anything it comes from the forecast — a hot day fills the pavement, road
works empty it, a downpour leaves one soul hurrying past under an umbrella.
Once the day has traded it comes from the glasses that actually crossed the
counter, so the report shows you the crowd you earned. In a heat wave they fan
themselves.
## Sound
Audio starts on the first gesture, as browsers require. The 1979 skin gets the
chiptune: two pulse voices, a triangle bass and filtered noise, with real
Fourier-built duty cycles rather than a plain square.
The remaster gets a band. Same engine, more of it — a sine kick with a pitch
envelope, a snare with body under the crack, sixteenth hats, and a sawtooth
bass through a sweeping resonant lowpass, which is where the funk actually
lives. Patterns are written in sixteenths with a light shuffle, and chords are
just slash-separated notes in a step (`F4/A4/C5`).
Both run off one scheduler that queues notes 200 ms ahead, so the groove does
not stutter when React re-renders. `MUSIC` and `SOUND` toggle independently.
## Two people, two browsers
There is no server and no shared state: the whole game is a static bundle and
lives entirely in the page. Two people on two machines, two browsers, or two
profiles are completely independent — different seeds, different weather,
different books.
The one thing two tabs in the *same* browser profile share is `localStorage`,
which holds the high score table and the skin. They stay in step: each tab
listens for the other's writes and refreshes rather than going stale.