Final Project — The Grove

1. Sketch, Code, and Clips

Repository

2. Concept

The Grove started as a browser-based resource management game built in P5.js for the midterm. The player tends to a small world made up of five locations — a world map, a river, a forest, a pottery studio, and a greenhouse — collecting materials, crafting pottery, and growing plants in a chain of interdependent steps.

For the final, the question was how to make the act of playing feel less like operating a computer and more like tending to something real. The answer was to rebuild every major interaction as a physical action. Shaping a pot became a gesture over a sensor. Watering a plant became literally pouring water. Digging for clay meant pressing a shovel into a contact board on the table.

The design principle throughout was physical metaphor — every sensor and prop was chosen because the physical action it captures mirrors what it triggers in the game. The player never uses a mouse or keyboard during gameplay. The entire installation is built around five physical components, each assigned to a specific location and moment in the loop.

3. Interaction Design
Joystick — Universal Navigation

A standard analog thumbstick module is the only navigation device in the game, replacing the mouse entirely. It handles all scene transitions, menu navigation, and in-scene cursor movement.

    • On the Map, left and right cycle through locations (Studio, Greenhouse, Forest, River). Pressing the button enters the selected location.
    • In any scene, pushing up enters the upper HUD zone where Return to Map and Menu buttons are highlighted. Pushing down enters the lower HUD zone (inventory). The button confirms the highlighted element.
    • In the Studio, left and right switch focus between the pottery wheel and the furnace.
    • In the River and Studio (when carrying a pot), the joystick moves a virtual cursor freely around the scene rather than switching focus.
    • In the Greenhouse, the joystick navigates a 4×8 grid of planting slots. Pushing up from the top row enters the upper HUD; pushing down from the bottom row enters the lower HUD.
    • In menus, up and down cycle through options and the button confirms.
Proximity Sensor — Pottery Wheel

An HC-SR04 ultrasonic sensor is mounted face-up at the Studio zone. To shape a pot, the player holds both hands above it with palms facing down, mimicking the gesture of cupping clay on a wheel. The closer the hands, the faster the pot advances through its shaping frames — making the interaction feel responsive and skill-based rather than binary. Pulling hands away pauses progress mid-shape.

Potentiometer — Furnace

A rotary dial at the Studio zone controls the kiln. Turning it up starts the fire, which loops the furnace audio and begins cooking the pot. The player watches the pot sprite on screen and turns the dial down to stop firing. If turned off in the right window (10–15 seconds), the pot is finished and ready to collect. Leave it too long and it burns, then turns to ash. The pot can only be picked up once the fire is off.

Conductive Shovel & Digging Board — Forest

A hand-sculpted clay shovel with an aluminum foil tip is used to dig for resources. A flat board on the table has five aluminum contact points — four corners and a center — each wired to a separate Arduino pin. When the shovel tip touches a pad, the circuit closes and the Arduino registers which plot was hit. Each plot is randomly assigned as clay or soil on every spawn and respawn. The player checks the screen to see what is at each spot before digging. Contact must be held for 200ms to prevent false triggers from grazing.

[Include schematic or photo of the digging board here]

Water Sensor — Greenhouse

A water sensor sits under a cup with the bottom cut out. After placing a seed in a greenhouse slot using the joystick, the player physically pours water into the cup. This is the only thing that triggers the watering animation and starts the plant growing. Pouring before planting discards the pour — the water only counts if there is a seed waiting for it. A debounce suppression prevents the sensor from re-triggering while the cup is still draining.

4. Arduino Code
Overview

The Arduino acts purely as a sensor hub. It reads all five physical inputs every loop iteration and transmits them as a single comma-separated line over serial at 9600 baud. It contains no game logic — all decisions about what sensor values mean are handled in P5.

Serial output format (11 fields, newline terminated):

joyX, joyY, joyBtn, proximity, potValue, waterValue, dig0, dig1, dig2, dig3, dig4
Pin Assignments
    • A0 – Joystick X axis
    • A1 – Joystick Y axis
    • A2 – Potentiometer (furnace dial)
    • A3 – Water sensor
    • D2–D6 – Dig contact points (INPUT_PULLUP), dig0 through dig4
    • D7 – Joystick button (INPUT_PULLUP)
    • D8 – HC-SR04 TRIG
    • D9 – HC-SR04 ECHO
Key Design Decisions
    • Proximity sensor: a rolling average of 5 readings is taken per loop call rather than averaging in a single blocking call, keeping loop time below 30ms.
    • Shovel contact: each pin must read LOW for 200ms continuously before the event is sent, preventing false triggers from brief grazes.
    • Water sensor: readings are suppressed for 4 seconds after a valid pour, giving the cup time to drain before the sensor is re-read.
    • Joystick: values are sent raw (0–1023 on each axis). Debounce and direction interpretation are handled on the P5 side.
4. P5.js Code
Architecture

The game is structured across seven files. globals.js declares all shared state. classes.js defines Plant, WateringEvent, and ResourcePlot. input.js handles serial communication, joystick reading, and all interaction logic. screens.js handles all scene rendering. audio.js manages background music and plant updates. ui.js provides HUD helpers, button drawing, and the reset and instruction functions. sketch.js is the P5 entry point.

Two stacked state variables drive everything. gameState controls the meta level (title screen, instructions, gameplay, pause). currentLayer controls which physical location the player is in. Every frame, draw() reads both and routes rendering and input accordingly.

Focus System

Rather than tracking a mouse position, the game maintains a focusZone variable (MAIN, UPPER_HUD, or LOWER_HUD) and a focusIndex within that zone. The joystick moves focus between zones and elements. The button always triggers whatever is currently focused. Each scene defines its own focusable elements and navigation rules. A pulsating orb drawn at the focused element gives the player constant visual feedback about where they are.

Virtual Cursor

In the River scene and when carrying a pot in the Studio, the joystick operates as a free-moving virtual cursor rather than a focus selector. A joyVirtualX and joyVirtualY position is updated every frame by mapping joystick deflection to a pixel-per-frame speed. This gives those scenes a more direct, physical feel.

Greenhouse Watering

The plant growth system uses a waiting flag on the Plant class. A plant created by joystick button press sets waiting = true, which causes update() to return early — the plant is visible on screen but frozen. The waterPoured flag is set exclusively inside parseSerial() when the Arduino sends a water reading above the threshold, using rising-edge detection to fire only once per pour. When waterPoured is true and a pendingWaterPlot exists, the watering animation plays and the plant’s waiting flag is cleared, starting its growth timer from that moment.

Furnace State Machine

The furnace moves through six states: EMPTY, PLACED, FIRING, READY_TO_COLLECT, BURNT, and ASH. A pot placed in the furnace starts in PLACED and waits for the potentiometer to cross the threshold before FIRING begins. While firing, the timer runs and sprite frames advance. Turning the dial down while the timer is in the 10–15 second window moves the state to READY_TO_COLLECT. The pot can only be collected when the fire is off. Leaving the fire on past 20 seconds burns the pot.

5. Arduino–P5 Communication

Communication runs over USB serial using the p5.webserial library. On game startup, a Connect Controller button appears below the canvas. Clicking it opens the browser’s port picker. Once connected, the port reads data at 9600 baud using port.readUntil(‘\n’), called every frame from draw(). Complete lines are passed to parseSerial(), which splits the CSV string and populates the sensorState object. All game code reads from sensorState rather than directly from mouse or keyboard events.

The Arduino sends one line every 50 milliseconds, giving approximately 20 updates per second. This is fast enough that joystick input feels immediate and proximity sensor changes are smooth.

No data is sent from P5 back to the Arduino. The Arduino has no awareness of the game state — it only reads and transmits. All interpretation happens in P5.

6. What I’m Particularly Proud Of
    • The physical metaphor is consistent throughout. Every prop was chosen because the action it requires mirrors what it does in the game — pouring water waters the plant, shaping clay shapes the pot.
    • The proximity-to-speed mapping on the pottery wheel. The closer your hands, the faster the pot forms. It makes a sensor interaction that could have been a simple on/off feel genuinely expressive.
    • The digging board and shovel as a circuit. The shovel is hand-sculpted clay with foil on the tip. The board has five foil pads. It looks like a prop from the world of the game, not a piece of electronics.
    • The watering system once it was finally correct — requiring a physical pour after placing a seed, with no false triggers and no carry-over between plants.
    • The overall installation feel. The game is played at a table with different physical zones for each location. Moving between scenes means physically shifting attention to a different part of the table.
7. Areas for Future Improvement
    • A spinning pottery wheel. The original vision included a small DC motor mounted beneath the wheel prop that would spin when the player cupped their hands over the proximity sensor. The motor would start when hands are detected and stop when they pull away, making the physical prop respond to the same input that drives the on-screen animation. This felt like one of the most natural extensions of the project but was cut due to time and the complexity of integrating motor control alongside the existing sensor setup.
    • Better failure feedback. When a planting action fails due to missing resources, nothing happens. A soft error tone or a brief highlight on the backpack would communicate the missing ingredient without breaking the calm.
    • The laptop-on-wheels concept. A motorized platform that rolls the screen to whichever table zone the player enters was part of the original design. A wheeled platform with powerful motors and zone-to-zone timing is buildable but was scoped out due to time.
    • Coordinate normalization. Every scene position is a hard-coded pixel value on a 1024×576 canvas. Normalizing all coordinates to proportions of width and height would make every scene scale to any display automatically.

Leave a Reply