Tamagotchi Cube

Interactive companion inspired by virtual pets and emotional caregiving

Tamagotchi Cube is an interactive companion device inspired by the nostalgic concept of virtual pets. Reimagined as a physical, cube-shaped object, it explores emotional bonding, care, and responsiveness through tangible interaction. By combining proximity sensing, RFID-based input, and expressive light behaviors, the project transforms the abstract idea of “care” into a visible, physical experience.

The cube possesses its own emotional states and personality. As users approach, it reacts to their presence, expressing awareness and anticipation through gentle changes in light. Over time, the cube develops needs, which the user must interpret and respond to. This dynamic creates a relationship built on observation, empathy, and repeated interaction.

Rather than relying on screens, Tamagotchi Cube communicates entirely through light and behavior. Emotions are expressed through color, brightness, and animated LED patterns, encouraging users to read subtle cues and engage intuitively. The project reflects on how humans form emotional attachments to simple systems and how care can be communicated through minimal, non-verbal feedback.

Process

Prototype design with 3D modeling with Solidwork
Figure Out the Material of Final Prototype 

Interaction Concept

Users care for the cube by understanding its emotional state and responding appropriately.

  • The cube detects the user’s proximity and reacts as they approach

  • It displays an emotional state using color

  • The user inserts a specialized RFID chip representing a form of care

  • Correct care results in a joyful visual reward

  • Incorrect care prompts a warning response

Through repeated exchanges, the cube becomes a companion that rewards attentiveness and emotional awareness.

System Overview

The installation integrates sensors, RFID technology, and LED outputs controlled by a microcontroller.

Input

  • Time-of-Flight Distance Sensor (VL53L0X)
    Detects the proximity of the user and initiates interaction

  • RFID Reader (MFRC522)
    Reads specialized care chips inserted into the cube

    • Food / Care / Activity chips (mapped to emotional needs)

Processing

  • Arduino Uno

    • Interprets distance and RFID data

    • Manages emotional state transitions

    • Controls LED brightness and animations

Output

  • NeoPixel LED Strip

    • Visualizes emotional states through color and motion

    • Smooth brightness transitions based on proximity

Emotional States

The cube communicates its internal state through color:

  • Pink (Neutral)
    Default state when the user is nearby but interaction has not yet begun

  • Red (Angry)
    Indicates the cube needs attention or specific care

  • Green (Steady)
    Calm and content, but still open to interaction

  • Rainbow (Happy)
    Triggered when the user provides the correct care
    A celebratory reward lasting approximately 20 seconds

Interaction Flow

  1. Approach
    The user approaches the cube

    • Distance sensor detects presence

    • Soft pink glow appears and increases in brightness

  2. Emotional Expression
    When the user is within close range (≈10 cm):

    • The cube randomly selects an emotional state

    • Red (angry) or green (steady) is displayed

  3. Care Action
    The user interprets the cube’s emotion

    • Inserts an RFID care chip through the top opening

  4. Response

    • Correct chip:
      The cube enters a joyful rainbow animation

    • Incorrect chip:
      The cube blinks red three times, signaling a mismatch

  5. Reset

    • The cube transitions into a new emotional state

    • Awaits the next interaction

  • #include <SPI.h>

    #include <MFRC522.h>

    #include <Adafruit_NeoPixel.h>

    #include <Wire.h>

    #include <Adafruit_VL53L0X.h>

    #define SS_PIN 10

    #define RST_PIN 9

    #define NEOPIXEL_PIN 7

    #define NUM_PIXELS 40

    #define TOTAL_PIXELS 80

    MFRC522 rfid(SS_PIN, RST_PIN);

    Adafruit_NeoPixel pixels(TOTAL_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

    Adafruit_VL53L0X lox = Adafruit_VL53L0X();

    unsigned long lastEmotionChangeTime = 0;

    const unsigned long EMOTION_CHANGE_INTERVAL = 10000; // Change emotion every 10 seconds

    byte tagIds[][4] = {

    {209, 221, 145, 73},

    {193, 223, 145, 73},

    {97, 220, 145, 73}

    };

    int currentEmotion = -1; // -1: Pink, 0: Angry, 1: Steady

    byte* correctTag;

    int currentBrightness = 1;

    void setup() {

    Serial.begin(115200);

    SPI.begin();

    rfid.PCD_Init();

    ‍ ‍

    pixels.begin();

    pixels.setBrightness(currentBrightness);

    setPink();

    turnOffExtraLEDs();

    ‍ ‍pixels.show();

    ‍ ‍

    Wire.begin();

    if (!lox.begin()) {

    Serial.println(F("Failed to boot VL53L0X"));

    while(1);

    }

    ‍ ‍

    randomSeed(analogRead(0));

    Serial.println("System initialized. Approach the VL53L0X sensor.");

    }

    void loop() {

    long distance = measureDistance();

    updateNeoPixelBasedOnDistance(distance);

    ‍ ‍

    if (distance <= 100) {

    if (currentEmotion == -1 && millis() - lastEmotionChangeTime > EMOTION_CHANGE_INTERVAL) {

    showRandomEmotion();

    lastEmotionChangeTime = millis();

    }

    handleRFIDTag();

    }

    ‍ ‍

    delay(100);

    }

    long measureDistance() {

    VL53L0X_RangingMeasurementData_t measure;

    lox.rangingTest(&measure, false);

    ‍ ‍

    if (measure.RangeStatus != 4) {

    return measure.RangeMilliMeter;

    } else {

    return 2000; // Return a large value when out of range

    }

    }

    void updateNeoPixelBasedOnDistance(long distance) {

    if (distance <= 1000) {

    int targetBrightness;

    if (distance > 100) { // Pink color with increasing brightness

    targetBrightness = map(distance, 1000, 100, 1, 255);

    targetBrightness = constrain(targetBrightness, 1, 255);

    currentEmotion = -1;

    } else { // Full brightness when distance <= 100 mm (10 cm)

    targetBrightness = 255;

    if (currentEmotion == -1) {

    showRandomEmotion();

    }

    }

    ‍ ‍

    // Smooth brightness transition

    if (currentBrightness < targetBrightness) {

    currentBrightness++;

    } else if (currentBrightness > targetBrightness) {

    currentBrightness--;

    }

    ‍ ‍

    pixels.setBrightness(currentBrightness);

    if (currentEmotion == -1) {

    setPink();

    }

    ‍ ‍pixels.show();

    }

    }

    void showRandomEmotion() {

    currentEmotion = random(2); // Only Angry (0) or Steady (1)

    correctTag = tagIds[currentEmotion];

    switch (currentEmotion) {

    case 0:

    setAngry();

    break;

    case 1:

    setSteady();

    break;

    }

    ‍ ‍pixels.show();

    Serial.println("New emotion displayed. Scan the correct RFID tag.");

    }

    void handleRFIDTag() {

    if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {

    if (checkTagMatch(rfid.uid.uidByte, correctTag)) {

    Serial.println("Correct tag! Well done!");

    setHappy(); // Set to happy state (rainbow effect for about 20 seconds)

    showRandomEmotion(); // Show new random emotion after happy state

    lastEmotionChangeTime = millis(); // Reset the timer

    } else {

    Serial.println("Wrong tag. Try again.");

    blinkAngry();

    }

    rfid.PICC_HaltA();

    rfid.PCD_StopCrypto1();

    }

    }

    void setPink() {

    for (int i = 0; i < NUM_PIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color(255, 20, 147)); // Deep pink

    }

    }

    void setAngry() {

    for (int i = 0; i < NUM_PIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color(255, 0, 0)); // Red for angry

    }

    }

    void setSteady() {

    for (int i = 0; i < NUM_PIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color(0, 255, 0)); // Green for steady

    }

    }

    void setHappy() {

    unsigned long startTime = millis();

    while (millis() - startTime < 20000) { // Run for about 20 seconds

    rainbowEffect();

    }

    }

    void turnOffExtraLEDs() {

    for (int i = NUM_PIXELS; i < TOTAL_PIXELS; i++) {

    pixels.setPixelColor(i, 0, 0, 0);

    }

    }

    bool checkTagMatch(byte* tagToCheck, byte* storedTag) {

    for (byte i = 0; i < 4; i++) {

    if (tagToCheck[i] != storedTag[i]) {

    return false;

    }

    }

    return true;

    }

    void blinkAngry() {

    for (int j = 0; j < 3; j++) {

    setAngry();

    ‍ ‍pixels.show();

    delay(200);

    pixels.clear();

    ‍ ‍pixels.show();

    delay(200);

    }

    // Restore the current emotion

    switch (currentEmotion) {

    case 0:

    setAngry();

    break;

    case 1:

    setSteady();

    break;

    default:

    setPink();

    break;

    }

    ‍ ‍pixels.show();

    }

    void rainbowEffect() {

    static int j = 0;

    for(int i = 0; i < NUM_PIXELS; i++) {

    int pixelHue = (i 65536L / NUM_PIXELS + j 256) & 0xFFFF;

    pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue)));

    }

    turnOffExtraLEDs();

    ‍ ‍pixels.show();

    delay(50); // Slower, more gentle transition

    j = (j + 1) % 256; // Slower color cycle

    }

How will people engage with your project?

People engage with Tamagotchi Cube through curiosity and care. By approaching, observing, and responding to its emotional cues, users form a playful yet meaningful relationship with the object. The project encourages attentiveness and empathy, rewarding users who take the time to understand and respond to subtle signals.

Rather than demanding constant interaction, the cube invites gentle, intentional engagement—mirroring the way emotional bonds form in real relationships.

Designed by Luna Park. 2025

Next
Next

Lantern of Connection