didi v2.0 of Liquid Left is a 2.5D atmospheric narrative exploration game adapted from Xindi's fiction Liquid Left. You play as didi, a sentient droplet navigating a breathing, organic world. Through a series of abstract, sensory stages, players explore themes of language, physical connection, and the cycle of nature.
"我不害怕。我不在了,湿也永远会在。"
"I am not afraid. Even if I am gone, the wetness will always remain."
The game is divided into chapters, each introducing unique mechanics that serve as metaphors for the droplet's life cycle:
- Prologue: Birth - Use slingshot physics to launch yourself towards the light.
- Ch 1: Language - Lure and connect neural nodes to weave the "language of liquid."
- Ch 2: Name - Burst bubbles and collect fragments to discover your identity.
- Ch 3: Chewing - Squeeze through tight organic spaces; growth comes from friction and resistance.
- Ch 4: Wind - Shield a withered leaf from wind bombardment to restore life.
- Ch 5: Travel - Choose your vehicle; emotional resonance dictates your path.
- Ch 6: Connection - Tether your body to nodes to form a skeletal structure.
- Ch 7: Home - Return to the lake of tears and merge with the collective.
- Finale: Sun - A ritual of evaporation and rain; the cycle completes.
This project is built with modern web technologies focusing on performance and procedural generation.
- Core: React 19 + TypeScript
- 3D Engine: Three.js via @react-three/fiber
- 3D Helpers & Abstractions: @react-three/drei
- State Management: Zustand
- Animation: Framer Motion (UI) & Custom Frame Loops (3D)
- Audio: Native Web Audio API (No external assets; all sound effects and ambience are synthesized procedurally in real-time).
- Styling: Tailwind CSS
- Testing: Vitest + @testing-library/react
- Build Tool: Vite
- Node.js 18+
- npm or yarn
# Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build for production
npm run build
# Preview production build
npm run preview├── components/ # React + Three.js components
│ ├── Player.tsx # Player physics and rendering
│ ├── World.tsx # Environment and level geometry
│ ├── Puzzle.tsx # Node system and connections
│ └── UI.tsx # 2D overlay UI
├── contexts/ # React Context providers
├── locales/ # i18n translations (zh/en)
├── utils/ # Utility functions
│ └── audio.ts # Procedural audio synthesis
├── store.ts # Zustand game state
├── App.tsx # Main app and camera system
└── src/
├── __tests__/ # Unit and integration tests
└── test/ # Test setup and mocks
We maintain comprehensive test coverage to ensure code quality. See TESTING.md for details.
# Run all tests
npm test
# Watch mode (for development)
npm test -- --watch
# UI mode (visual test runner)
npm run test:ui
# Coverage report
npm run test:coverageCurrent test coverage: 57.69% (store.ts core logic)
Game mechanics, physics, and camera settings are now configurable via constants. See REFACTORING.md for the complete guide.
Quick examples:
// Adjust physics feel (components/Player.tsx)
const PHYSICS_CONFIG = {
MOBILE_MAX_FORCE: 10.0, // Increase for more responsive mobile controls
DAMPING: 0.92 // Decrease for more inertia
};
// Adjust camera view (App.tsx)
const CAMERA_CONFIG = {
CHEWING: { offset: [10, 20, 10], baseZoom: 60 } // Close-up view
};Keyboard Shortcuts (dev mode):
1-9: Jump to specific level+/-: Zoom in/outAlt + Drag: Alternative camera control
Browser DevTools:
// Access game state (dev mode)
window.__GAME_DEBUG__?.getState()
window.__GAME_DEBUG__?.teleportToLevel('WIND')- Mouse/Touch: The primary input method.
- Lure: Hold Left Click to attract the droplet.
- Slingshot: Drag back and release to launch.
- Interact: Click to pop bubbles, trigger rain, or connect nodes.
- Cursor: A custom glowing cursor provides feedback on interaction states.
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
Based on the fiction Liquid Left by Xindi Zhou.
Navigating the breathing, organic world of Liquid Left.

