Start: In Class Due: Next Class
Create a relaxing, soothing ambient animation that plays by itself - then add touch interaction that lets the user affect it. When the user stops touching, it gracefully returns to its peaceful default state.
| Concept | Default Animation | Touch Interaction |
|---|---|---|
| Snow Globe | Gentle snowfall drifting down, settling at bottom | Touch to "shake" - snow swirls chaotically, then slowly settles again |
| Koi Pond | Fish swim lazily, ripples expand slowly | Touch to "feed" - fish rush to touch point, then disperse |
| Fireplace | Gentle flickering flames, occasional crackle | Touch to "stoke" - flames grow larger/brighter, sparks fly up |
| Beach Waves | Waves roll in and out rhythmically | Touch to "throw a stone" - ripples expand from touch point |
| Starfield | Stars twinkle gently, occasional shooting star | Touch to "warp speed" - stars streak past, then slow down |
| Rain on Window | Raindrops slide down, slight blur effect | Touch to "draw in fog" - leave temporary trails that fade |
| Lava Lamp | Blobs rise and fall slowly, merge and split | Touch to "heat up" - blobs move faster, colors shift |
| Wind Chimes | Lines sway gently in breeze | Touch to "blow" - chimes swing wildly from touch direction |
| Candle | Flame flickers softly, wax drips occasionally | Touch to "blow" - flame bends away, almost goes out, recovers |
| Zen Garden | Rake lines shimmer slightly | Touch to "rake" - draw new patterns in the sand |
These are just starting points! Invent your own. What relaxes YOU?
| Requirement | Details |
|---|---|
| 1. Default Animation | Must run continuously without any touch input. Should feel peaceful, ambient, "alive." |
| 2. Touch Response | At least ONE touch input must visibly change the animation. The change should feel connected to the concept. |
| 3. Return to Default | When touch stops, animation must smoothly transition back to default state (not snap instantly). |
| 4. Uses Cap Touch | Must use at least one capacitive touch pin (GPIO 1-4). Can use more for richer interaction. |
| 5. Iteration | Include version number in your code comments. Show your process! |
| Criteria | Points | What I'm Looking For |
|---|---|---|
| Ambient Quality | 25 | Does the default animation feel calming? Would you actually want this on your desk? |
| Interaction Design | 25 | Does the touch interaction make sense for the concept? Is it satisfying? |
| Smooth Transitions | 20 | Does it transition gracefully between states? No jarring jumps? |
| Technical Execution | 15 | Does it run smoothly? Is the code organized? Are there comments? |
| Creativity | 15 | Did you make it your own? Surprise me! |
These are the building blocks from Projects 1-5. Mix and match!
| Pattern | From Project | Use It For |
|---|---|---|
touchRead(pin) > threshold |
Project 1 | Detecting touch |
value = value * 0.95 + target * 0.05 |
Projects 3-5 | Smooth transitions (interpolation) |
millis() - lastTouchTime > TIMEOUT |
Project 4 | Detecting "stopped touching" |
sin(timeStep * speed) |
Projects 3-5 | Oscillating/pulsing animations |
random(min, max) |
Projects 4-5 | Natural variation, particle spawning |
struct Particle { x, y, vx, vy... } |
Projects 4-5 | Multiple moving objects |
Here's a sequence of prompts to help you build your toy piece by piece:
| Step | Gemini Prompt Template |
|---|---|
| 1. Concept | "I want to make a [YOUR CONCEPT] animation on a 128x64 OLED. Describe what visual elements I'd need and how they'd move." |
| 2. Static Scene | "Write ESP32 Arduino code using Adafruit SH1106G to draw a static [YOUR SCENE] on the OLED." |
| 3. Basic Animation | "Make the [ELEMENTS] move. Use sin() for smooth oscillation or random() for natural variation." |
| 4. Add Particles | "Add an array of [PARTICLES] that [BEHAVIOR]. Use a struct with x, y, speed variables." |
| 5. Add Touch Detection | "Add capacitive touch detection on GPIO 1-4. When touched, [WHAT SHOULD CHANGE]?" |
| 6. Smooth Return | "When touch stops, smoothly interpolate back to default state over ~2 seconds using the formula: value = value * 0.98 + target * 0.02" |
| 7. Polish | "Add [SMALL DETAIL] to make it feel more alive. Examples: occasional random events, subtle color/brightness changes, secondary animations." |
Here's a simple starting point showing the pattern. This is NOT complete - it's just to show the structure:
/* * AmbientTouchToy_Starter.ino * V1: Basic Structure Example * * This is a STARTING POINT - not a finished project! * Use this pattern, but make your own concept. */ #include <Adafruit_GFX.h> #include <Adafruit_SH110X.h> #include <Wire.h> Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1); // Touch pins const int TOUCH_PIN = 1; // Use more pins for more interactions! const int TOUCH_THRESHOLD = 80000; // Animation state float intensity = 0.5; // Current intensity (0.0 to 1.0) float targetIntensity = 0.5; // Where we're heading float defaultIntensity = 0.5; // Peaceful default float activeIntensity = 1.0; // When touching float timeStep = 0; unsigned long lastTouchTime = 0; const int RETURN_DELAY = 1000; // ms before returning to default void setup() { Serial.begin(115200); Wire.begin(5, 6); Wire.setClock(400000); display.begin(0x3C, true); display.clearDisplay(); display.display(); } void loop() { timeStep += 0.05; // === 1. READ TOUCH === bool touching = touchRead(TOUCH_PIN) > TOUCH_THRESHOLD; // === 2. UPDATE TARGET BASED ON TOUCH === if (touching) { lastTouchTime = millis(); targetIntensity = activeIntensity; // Ramp up! } else { // Return to default after delay if (millis() - lastTouchTime > RETURN_DELAY) { targetIntensity = defaultIntensity; } } // === 3. SMOOTH INTERPOLATION === // This is the key! Never jump, always glide. intensity = intensity * 0.95 + targetIntensity * 0.05; // === 4. DRAW YOUR ANIMATION === display.clearDisplay(); // TODO: Replace this with YOUR animation! // Use 'intensity' to control how active/calm things are // Example: A simple pulsing circle that grows when touched int baseSize = 10; int pulseAmount = 5 * intensity; int pulse = sin(timeStep * (1 + intensity * 3)) * pulseAmount; display.drawCircle(64, 32, baseSize + pulse + (intensity * 15), SH110X_WHITE); // Show intensity for debugging (remove in final version) display.setTextSize(1); display.setTextColor(SH110X_WHITE); display.setCursor(0, 0); display.print("Int: "); display.print(intensity, 2); display.display(); delay(16); }
| Tip | Why It Matters |
|---|---|
| Start Simple | Get ONE thing moving first. Add complexity in layers. Don't try to build everything at once. |
| Use Intensity | Have a single "intensity" variable (0.0 to 1.0) that controls how active everything is. Touch increases it, release decreases it. |
| Smooth Everything | Never set values directly. Always interpolate: value = value * 0.95 + target * 0.05 |
| Natural Variation | Add small random variations to timing, position, size. Nature is never perfectly regular. |
| Test on People | Show someone and watch their face. Do they smile? Do they keep touching it? That's your feedback. |
Submit a short video (30-60 seconds) showing:
| What to Show | Why |
|---|---|
| 1. Default animation running | Show your ambient animation in its peaceful, untouched state for a few seconds |
| 2. Touch interaction | Demonstrate touching the wires and show how the animation responds |
| 3. Release and return | Let go and show the smooth transition back to the default state |
| 4. Your hands visible | I want to see YOU interacting with YOUR creation! |