Week 3 Assignment: Ambient Touch Toy

Start: In Class    Due: Next Class


The Brief

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.

Think: Something you'd put on your desk and glance at occasionally. Something calming. Something that makes you smile when you interact with it, then settles back down.

Inspiration Examples

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?


Requirements

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!

Grading Rubric

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!

Technical Patterns to Use

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

Getting Started with Gemini

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."

Example: Minimal Fireplace Starter

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);
}
This starter is intentionally boring! It's just a pulsing circle. Your job is to replace it with something beautiful and meaningful to you.

Tips for Success

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.

Submission

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!
Tips for good video: Film in landscape mode. Make sure the OLED screen is visible and in focus. Natural lighting works best. You can use your phone propped up or ask a friend to film.

Questions to Ask Yourself