# Project export: Fable

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: Fable tells stories that adapt to your state of mind. As your brainwaves shift, we generate a story and create an immersive visual journey that mirrors your inner state in real time.
- Devpost: https://devpost.com/software/fable-br8e1d
- GitHub: https://github.com/StiopaPopa/fable
- Video: https://www.youtube.com/embed/Q8udHqdKbKw?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (OpenAI: Most Creative Use of OpenAI API (OpenAI SF HQ lunch + Swag))
- Team: 2 GitHub contributor(s) — StiopaPopa (10 commits), max (3 commits)

## Devpost submission (written by the team)

### Inspiration

💡 Fable began with a simple question: What if a story could adapt to your state of mind? As our team explored EEG technology, we became excited about the idea of measuring brain waves and uncovering hidden patterns of thought. What if a narrative could respond to your level of focus and relaxation—your shifting mental state? With EEG headsets reading brain activity, Fable dynamically shapes its plot, characters, sound effects, and visuals, crafting a living, breathing story that unfolds uniquely for you. This isn’t just storytelling—it’s an experience powered by your mind.

### What it does

🔮 Fable reads your brainwaves to craft a living story that responds to your every thought. As your mental state shifts, the plot dynamically evolves—changing tone, pacing, or twists to reflect how you feel. Simultaneously, the visuals adapt to match the unfolding narrative, immersing you in a real-time, brain-driven adventure where each scene is shaped by your mind. Even the moving gradient color background gradually fades and shifts, mirroring your inner state and heightening the immersive feel of your journey.

### How we built it

🧱 We built Fable by combining a Muse 2 EEG headset with a dynamic storytelling pipeline, powered by a Python FastAPI backend and a Next.js frontend styled with Tailwind CSS. The headset reads beta, alpha, theta, and gamma wave patterns—interpreted as relaxed, neutral, or focused states—using Mind Monitor and streams this data in real time through OCS. Our main story loop runs every 30 seconds, checking the user’s current state and using the OpenAI API to generate story scripts (and corresponding sound effect prompts) based on those EEG readings. We map each state to a specific narrative direction—for neutral, we steer the story toward fresh discoveries; for focused, we introduce more details or challenges; and for relaxed, we invite ease and wonder. To enhance immersion, we integrated 3.js and GLSL shaders to create an EEG-based dynamic gradient background that shifts in real time with the user’s mind state. We then feed both the text-to-speech lines and OpenAI-generated SFX prompts into ElevenLabs (streamed via WebSocket), allowing us to seamlessly produce both TTS dialogue and ambient sound effects. Finally, we bring the story to life visually with the Luma Labs API. Throughout this process, we employ a multithreading approach to process text, voice, and video asynchronously, ensuring smooth, parallel generation of each element.

### Challenges we ran into

🚧 We faced several hurdles bringing Fable to life. Hooking up OpenAI’s story generation so that each paragraph was generated sentence by sentence required careful orchestration within our webapp. Integrating ElevenLabs’ text-to-speech to ensure that audio and subtitles streamed seamlessly in real time was another challenge. Working around lengthy inference times on video generation models forced us to asynchronously and concurrently queue and segment videos to keep up with the script and audio. Our hacking led us to benchmark at least 10 different video generation APIs against each other for speed, including text-to-image and image-to-video generation. We also developed our own text-to-image and image-to-text pipeline to improve efficiency and control over the entire process.

### Accomplishments we're proud of

🏆 We’re proud of creating a seamless, real-time storytelling platform that translates brainwave data into dynamically shifting narratives, audio, and visuals. By integrating multiple APIs—Muse for EEG, OpenAI for generative text, ElevenLabs for audio, and Luma Labs for visuals—we managed to build an immersive, multi-sensory experience that feels both personalized and technically robust. And most of all, we love the experience of listening to the stories that come out of our product!

### What we learned

📚 Through developing Fable, we gained a deeper understanding of real-time data processing, from parsing EEG signals to synchronizing audio and video outputs. Integrating diverse tools like OpenAI, ElevenLabs, and Luma Labs taught us the value of modular design and clear communication between APIs. We also discovered how critical it is to balance technical complexity with user experience, ensuring that the shifting storyline remains both immersive and coherent.

### What's next

🚀 We’re excited to broaden Fable’s capabilities by refining our EEG interpretation for an even wider range of emotions and deeper engagement tracking, exploring additional wearable sensors beyond the Muse headset, and advancing our storytelling techniques—potentially introducing multiple branching storylines, co-op experiences, and VR integration.

## README (from the GitHub repository)

# Fable [TreeHacks OpenAI Prize Winner]

## Inspiration 💡
Fable began with a simple question: What if a story could adapt to your state of mind? As our team explored EEG technology, we became excited about the idea of measuring brain waves and uncovering hidden patterns of thought. What if a narrative could respond to your level of focus and relaxation—your shifting mental state? With EEG headsets reading brain activity, Fable dynamically shapes its plot, characters, sound effects, and visuals, crafting a living, breathing story that unfolds uniquely for you. This isn’t just storytelling—it’s an experience powered by your mind.

## What it does 🔮
Fable reads your brainwaves to craft a living story that responds to your every thought. As your mental state shifts, the plot dynamically evolves—changing tone, pacing, or twists to reflect how you feel. Simultaneously, the visuals adapt to match the unfolding narrative, immersing you in a real-time, brain-driven adventure where each scene is shaped by your mind. Even the moving gradient color background gradually fades and shifts, mirroring your inner state and heightening the immersive feel of your journey.

## How we built it 🧱
We built Fable by combining a Muse 2 EEG headset with a dynamic storytelling pipeline, powered by a Python FastAPI backend and a Next.js frontend styled with Tailwind CSS. The headset reads beta, alpha, theta, and gamma wave patterns—interpreted as relaxed, neutral, or focused states—using Mind Monitor and streams this data in real time through OCS. Our main story loop runs every 30 seconds, checking the user’s current state and using the OpenAI API to generate story scripts (and corresponding sound effect prompts) based on those EEG readings. We map each state to a specific narrative direction—for neutral, we steer the story toward fresh discoveries; for focused, we introduce more details or challenges; and for relaxed, we invite ease and wonder. To enhance immersion, we integrated 3.js and GLSL shaders to create an EEG-based dynamic gradient background that shifts in real time with the user’s mind state. We then feed both the text-to-speech lines and OpenAI-generated SFX prompts into ElevenLabs (streamed via WebSocket), allowing us to seamlessly produce both TTS dialogue and ambient sound effects. Finally, we bring the story to life visually with the Luma Labs API. Throughout this process, we employ a multithreading approach to process text, voice, and video asynchronously, ensuring smooth, parallel generation of each element.

## Challenges we ran into 🚧
We faced several hurdles bringing Fable to life. Hooking up OpenAI’s story generation so that each paragraph was generated sentence by sentence required careful orchestration within our webapp. Integrating ElevenLabs’ text-to-speech to ensure that audio and subtitles streamed seamlessly in real time was another challenge. Working around lengthy inference times on video generation models forced us to asynchronously and concurrently queue and segment videos to keep up with the script and audio. Our hacking led us to benchmark at least 10 different video generation APIs against each other for speed, including text-to-image and image-to-video generation.

## Accomplishments that we're proud of 🏆
We’re proud of creating a seamless, real-time storytelling platform that translates brainwave data into dynamically shifting narratives, audio, and visuals. By integrating multiple APIs—Muse for EEG, OpenAI for generative text, ElevenLabs for audio, and Luma Labs for visuals—we managed to build an immersive, multi-sensory experience that feels both personalized and technically robust. And most of all, we love the experience of listening to the stories that come out of our product!

## What we learned 📚
Through developing Fable, we gained a deeper understanding of real-time data processing, from parsing EEG signals to synchronizing audio and video outputs. Integrating diverse tools like OpenAI, ElevenLabs, and Luma Labs taught us the value of modular design and clear communication between APIs. We also discovered how critical it is to balance technical complexity with user experience, ensuring that the shifting storyline remains both immersive and coherent.

## What's next for Fable 🚀 
We’re excited to broaden Fable’s capabilities by refining our EEG interpretation for an even wider range of emotions and deeper engagement tracking, exploring additional wearable sensors beyond the Muse headset, and advancing our storytelling techniques—potentially introducing multiple branching storylines, co-op experiences, and VR integration.


## Detected evidence (automated analysis)

Indexed codebase: 3814 recognized source files, 49034 KB.
- C (language) — detected in the code
- C++ (language) — detected in the code
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 4935)

```
backend/.env
backend/eeg.py
backend/env/bin/activate
backend/env/bin/activate.csh
backend/env/bin/activate.fish
backend/env/bin/Activate.ps1
backend/env/bin/distro
backend/env/bin/dotenv
backend/env/bin/email_validator
backend/env/bin/f2py
backend/env/bin/fastapi
backend/env/bin/httpx
backend/env/bin/markdown-it
backend/env/bin/normalizer
backend/env/bin/numpy-config
backend/env/bin/openai
backend/env/bin/pip
backend/env/bin/pip3
backend/env/bin/pip3.12
backend/env/bin/pygmentize
backend/env/bin/python
backend/env/bin/python3
backend/env/bin/python3.12
backend/env/bin/tqdm
backend/env/bin/typer
backend/env/bin/uvicorn
backend/env/bin/watchfiles
backend/env/lib/python3.12/site-packages/_yaml/__init__.py
backend/env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER
backend/env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE
backend/env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA
backend/env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD
backend/env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL
backend/env/lib/python3.12/site-packages/annotated_types/__init__.py
backend/env/lib/python3.12/site-packages/annotated_types/py.typed
backend/env/lib/python3.12/site-packages/annotated_types/test_cases.py
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/entry_points.txt
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/INSTALLER
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/LICENSE
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/METADATA
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/RECORD
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/top_level.txt
backend/env/lib/python3.12/site-packages/anyio-4.8.0.dist-info/WHEEL
backend/env/lib/python3.12/site-packages/anyio/__init__.py
backend/env/lib/python3.12/site-packages/anyio/_backends/__init__.py
backend/env/lib/python3.12/site-packages/anyio/_backends/_asyncio.py
backend/env/lib/python3.12/site-packages/anyio/_backends/_trio.py
backend/env/lib/python3.12/site-packages/anyio/_core/__init__.py
backend/env/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py
backend/env/lib/python3.12/site-packages/anyio/_core/_eventloop.py
backend/env/lib/python3.12/site-packages/anyio/_core/_exceptions.py
backend/env/lib/python3.12/site-packages/anyio/_core/_fileio.py
backend/env/lib/python3.12/site-packages/anyio/_core/_resources.py
backend/env/lib/python3.12/site-packages/anyio/_core/_signals.py
backend/env/lib/python3.12/site-packages/anyio/_core/_sockets.py
backend/env/lib/python3.12/site-packages/anyio/_core/_streams.py
backend/env/lib/python3.12/site-packages/anyio/_core/_subprocesses.py
backend/env/lib/python3.12/site-packages/anyio/_core/_synchronization.py
backend/env/lib/python3.12/site-packages/anyio/_core/_tasks.py
backend/env/lib/python3.12/site-packages/anyio/_core/_testing.py
backend/env/lib/python3.12/site-packages/anyio/_core/_typedattr.py
backend/env/lib/python3.12/site-packages/anyio/abc/__init__.py
backend/env/lib/python3.12/site-packages/anyio/abc/_eventloop.py
backend/env/lib/python3.12/site-packages/anyio/abc/_resources.py
backend/env/lib/python3.12/site-packages/anyio/abc/_sockets.py
backend/env/lib/python3.12/site-packages/anyio/abc/_streams.py
backend/env/lib/python3.12/site-packages/anyio/abc/_subprocesses.py
backend/env/lib/python3.12/site-packages/anyio/abc/_tasks.py
backend/env/lib/python3.12/site-packages/anyio/abc/_testing.py
backend/env/lib/python3.12/site-packages/anyio/from_thread.py
backend/env/lib/python3.12/site-packages/anyio/lowlevel.py
backend/env/lib/python3.12/site-packages/anyio/py.typed
backend/env/lib/python3.12/site-packages/anyio/pytest_plugin.py
backend/env/lib/python3.12/site-packages/anyio/streams/__init__.py
backend/env/lib/python3.12/site-packages/anyio/streams/buffered.py
backend/env/lib/python3.12/site-packages/anyio/streams/file.py
backend/env/lib/python3.12/site-packages/anyio/streams/memory.py
backend/env/lib/python3.12/site-packages/anyio/streams/stapled.py
backend/env/lib/python3.12/site-packages/anyio/streams/text.py
backend/env/lib/python3.12/site-packages/anyio/streams/tls.py
backend/env/lib/python3.12/site-packages/anyio/to_interpreter.py
backend/env/lib/python3.12/site-packages/anyio/to_process.py
backend/env/lib/python3.12/site-packages/anyio/to_thread.py
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/INSTALLER
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/LICENSE
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/METADATA
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/RECORD
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/top_level.txt
backend/env/lib/python3.12/site-packages/certifi-2025.1.31.dist-info/WHEEL
backend/env/lib/python3.12/site-packages/certifi/__init__.py
backend/env/lib/python3.12/site-packages/certifi/__main__.py
backend/env/lib/python3.12/site-packages/certifi/cacert.pem
backend/env/lib/python3.12/site-packages/certifi/core.py
backend/env/lib/python3.12/site-packages/certifi/py.typed
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/entry_points.txt
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/INSTALLER
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/LICENSE
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/METADATA
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/RECORD
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/top_level.txt
backend/env/lib/python3.12/site-packages/charset_normalizer-3.4.1.dist-info/WHEEL
backend/env/lib/python3.12/site-packages/charset_normalizer/__init__.py
backend/env/lib/python3.12/site-packages/charset_normalizer/__main__.py
backend/env/lib/python3.12/site-packages/charset_normalizer/api.py
backend/env/lib/python3.12/site-packages/charset_normalizer/cd.py
backend/env/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py
backend/env/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py
backend/env/lib/python3.12/site-packages/charset_normalizer/constant.py
backend/env/lib/python3.12/site-packages/charset_normalizer/legacy.py
backend/env/lib/python3.12/site-packages/charset_normalizer/md.py
backend/env/lib/python3.12/site-packages/charset_normalizer/models.py
backend/env/lib/python3.12/site-packages/charset_normalizer/py.typed
backend/env/lib/python3.12/site-packages/charset_normalizer/utils.py
backend/env/lib/python3.12/site-packages/charset_normalizer/version.py
backend/env/lib/python3.12/site-packages/click-8.1.8.dist-info/INSTALLER
backend/env/lib/python3.12/site-packages/click-8.1.8.dist-info/LICENSE.txt
backend/env/lib/python3.12/site-packages/click-8.1.8.dist-info/METADATA
backend/env/lib/python3.12/site-packages/click-8.1.8.dist-info/RECORD
backend/env/lib/python3.12/site-packages/click-8.1.8.dist-info/WHEEL
backend/env/lib/python3.12/site-packages/click/__init__.py
[4815 more files omitted for size]
```

### Dependencies

- frontend_/package.json: @eslint/eslintrc@^3, @react-three/drei@^9.121.5, @react-three/fiber@^8.17.14, @thi.ng/color@^5.7.24, @types/node@22.13.4, @types/react@19.0.8, chart.js@^4.4.7, elevenlabs@^1.51.0, eslint@^9, eslint-config-next@15.1.7, gsap@^3.12.7, leva@^0.10.0, next@15.1.7, postcss@^8, react@^19.0.0, react-chartjs-2@^5.3.0, react-dom@^19.0.0, stream@^0.0.3, tailwindcss@^3.4.1, three@^0.173.0, tone@^15.0.4
- package.json: @gsap/react@^2.1.2, @react-three/drei@^9.121.5, @react-three/fiber@^8.17.14, @thi.ng/color@^5.7.24, @types/three@^0.173.0, gsap@^3.12.7, leva@^0.10.0, three@^0.173.0

### Recent commits (newest first)

- minor change
- Update README.MD
- Update README.MD
- Update README.MD
- Create README.MD
- Update OSC_focus_relax_smoother.py
- Add files via upload
- Add files via upload
- Create placeholder.txt
- Add files via upload
- Create placeholder.txt
- Add files via upload
- Create placeholder.txt
- Add files via upload
- Create placeholder.txt
- first commit v2
- rd2
- EEG reading + mood BG gradient

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/env/lib/python3.12/site-packages/idna-3.10.dist-info/LICENSE.md

```markdown
BSD 3-Clause License

Copyright (c) 2013-2024, Kim Davies and contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

```

### backend/env/lib/python3.12/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md

```markdown
Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

```

### package.json

```
{
  "dependencies": {
    "@gsap/react": "^2.1.2",
    "@react-three/drei": "^9.121.5",
    "@react-three/fiber": "^8.17.14",
    "@thi.ng/color": "^5.7.24",
    "@types/three": "^0.173.0",
    "gsap": "^3.12.7",
    "leva": "^0.10.0",
    "three": "^0.173.0"
  }
}

```

### frontend_/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@react-three/drei": "^9.121.5",
    "@react-three/fiber": "^8.17.14",
    "@thi.ng/color": "^5.7.24",
    "chart.js": "^4.4.7",
    "elevenlabs": "^1.51.0",
    "gsap": "^3.12.7",
    "leva": "^0.10.0",
    "next": "15.1.7",
    "react": "^19.0.0",
    "react-chartjs-2": "^5.3.0",
    "react-dom": "^19.0.0",
    "stream": "^0.0.3",
    "three": "^0.173.0",
    "tone": "^15.0.4"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "22.13.4",
    "@types/react": "19.0.8",
    "eslint": "^9",
    "eslint-config-next": "15.1.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1"
  }
}

```

### backend/main.py

```python
import random
import math
import threading
from queue import Queue
import asyncio
import time
from collections import Counter
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from pythonosc import dispatcher, osc_server
import openai
from openai import OpenAI
import os
from dotenv import load_dotenv, dotenv_values 
from elevenlabs import stream
from elevenlabs.client import ElevenLabs

# 1) Import Luma AI
from lumaai import LumaAI

load_dotenv()

# ---------------------------
# Global Settings & Variables
# ---------------------------
ip = os.getenv("IP")  # e.g., "0.0.0.0" or "127.0.0.1"
port = 5000

# For your OpenAI key
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

tts_client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))


possible_ids = [
    "LruHrtVF6PSyGItzMNHS"
]
idx = random.randint(0,len(possible_ids) - 1)

VOICE_ID = possible_ids[idx]
MODEL_ID = "eleven_multilingual_v2"

# Luma AI client
luma_client = LumaAI(auth_token=os.getenv("LUMA_AI_TOKEN"))

# Global data arrays (if needed for smoothing, etc.)
tts_queue = Queue()
plot_val_count = 200
alpha_beta_ratios = []
smoothed_ratios = []
ema_alpha = 0.2  # Weight for exponential moving average

# Global state for classification (latest instantaneous classification)
latest_state = "NEUTRAL"
latest_smoothed_ratio = None

# EEG variables
hsi = [4, 4, 4, 4]
abs_waves = [-1, -1, -1, -1, -1]
rel_waves = [-1, -1, -1, -1, -1]

# Lock for thread-safe operations
data_lock = threading.Lock()

# =============== For 30s Aggregation + Story ===============
data_log = []  # will hold tuples of (timestamp, "FOCUSING"/"RELAXING"/"NEUTRAL")
story_so_far = ""
story_lock = threading.Lock()  # if we want to lock around story text

# This toggles if you want "meditative" style stories or not
MEDITATE = False

# 2) Store the latest video URL for the frontend
latest_video_url = None

# ---------------------------
# Smoothing & Classification
# ---------------------------
def exponential_moving_average(data, alpha=0.2):
    """Compute EMA on the last data point in 'data' list."""
    if not smoothed_ratios:
        return data[-1]
    return alpha * data[-1] + (1 - alpha) * smoothed_ratios[-1]

def classify_focus_vs_relaxation(smoothed_value):
    """
    Determine the mental state based on the smoothed ratio.
    Example thresholds:
      - < 1.4 => FOCUSING
      - > 2.0 => RELAXING
      - else  => NEUTRAL
    """
    if smoothed_value is None:
        return "NEUTRAL"  # no data yet

    if smoothed_value < 1.4:
        return "FOCUSING"
    elif smoothed_value > 2.0:
        return "RELAXING"
    else:
        return "NEUTRAL"

def get_label_for_last_30_seconds():
    """
    Returns the most common label from the last 30 seconds in data_log.
    If there's insufficient data, return 'NEUTRAL'.
    """
    cutoff = time.time() - 30.0
    with data_lock:
        recent_labels = [label for (t, label) in data_log if t >= cutoff]
    if len(recent_labels) < 5:
        return "NEUTRAL"
    counter = Counter(recent_labels)
    most_common_label, _ = counter.most_common(1)[0]
    return most_common_label

# ---------------------------
# OPENAI & Story Generation
# ---------------------------
def generate_next_segment(mental_state, previous_story):
    """
    Generate the next chunk (~60 words for ~30 seconds of speech).
    Adjust the prompt depending on MEDITATE or not.
    """

    if not previous_story:
        previous_part_text = (
            "This is the beginning of the story. There's no previous section yet."
        )
    else:
        previous_part_text = f"Previous part of the story:\n\"\"\"{previous_story}\"\"\""

    # Example meditation or normal story:
    if MEDITATE:
        prompt = f"""
You are a skilled, soothing meditation guide and storyteller. The user's current mental state is {mental_state}.
- If NEUTRAL, gently guide them toward deeper relaxation.
- If FOCUSING, channel that focus into mindful awareness.
- If RELAXING, encourage them to explore peaceful sensations more fully.

Continue the meditation story from the previous section, seamlessly adapting to the user's current state.
Write ~60 words in a calm, flowing tone. Avoid breaking the fourth wall.

{previous_part_text}

What happens next?
"""
    else:
        prompt = f"""
You are a masterful, imaginative storyteller who crafts plots that adapt to the user's mental state.
That state is {mental_state}, which can be NEUTRAL, FOCUSING, or RELAXING.
- If NEUTRAL, steer the story toward new discoveries, piquing curiosity.
- If FOCUSING, channel that concentration to reveal details or challenges, adding tension.
- If RELAXING, deepen a sense of ease and wonder, letting the adventure flow gently.

Continue the story from the previous section, focusing on developing the plot and aligning with the user's mental state.
Aim for ~60 words (about 30 seconds of speech at 120 WPM).
Keep the style engaging, immersive, and continuous. Avoid breaking the fourth wall.

{previous_part_text}

What happens next?
"""

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",  # or "gpt-4" or "gpt-3.5-turbo" if you have access
            messages=[{"role": "system", "content": prompt.strip()}],
            temperature=0.7,
        )
        return response.choices[0].message.content.strip()
    except Exception as e:
        print("OpenAI error:", e)
        return "[OpenAI Error]"

# ---------------------------
# Text-to-Speech Loop
# ---------------------------
def tts_loop():
    while True:
        text_segment = tts_queue.get()
        try:
            audio_stream = tts_client.text_to_speech.convert_as_stream(
                text=text_segment,
                voice_id=VOICE_ID,
                model_id=MODEL_ID
            )
            stream(audio_stream)
        except Exception as e:
            print(f"Error in TTS playback: {e}")
        finally:
            tts_queue.task_d
[truncated — 7438 more characters]
```

### frontend_/app/layout.js

```javascript
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}

```

### frontend_/app/page.js

```javascript
import EEGStream from "./components/EEGStream";
import AnimatedBackground from "./components/AnimatedBackground";
import StoryView from "./components/StoryView";
import VideoDemo from "./components/VideoDemo";

export default function Home() {
  return (
    <>
      <AnimatedBackground />
      <div className="relative grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
        <EEGStream />
        <VideoDemo />
        {/* <StoryView /> */}
      </div>
      {/* Footer title overlay */}
      <div className="pointer-events-none absolute bottom-0 w-full flex justify-center">
        <div className="relative">
          {/* The gradient overlay to partially cover the title */}
          <div className="absolute inset-0"></div>
          <h1
            className="relative text-9xl font-extrabold tracking-tight text-gray-500 opacity-40"
            style={{
              fontFamily:
                '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
            }}
          >
            Fable
          </h1>
        </div>
      </div>
    </>
  );
}

```

### backend/env/lib/python3.12/site-packages/fastapi/cli.py

```python
try:
    from fastapi_cli.cli import main as cli_main

except ImportError:  # pragma: no cover
    cli_main = None  # type: ignore


def main() -> None:
    if not cli_main:  # type: ignore[truthy-function]
        message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n'
        print(message)
        raise RuntimeError(message)  # noqa: B904
    cli_main()

```

### backend/env/lib/python3.12/site-packages/dotenv/cli.py

```python
import json
import os
import shlex
import sys
from contextlib import contextmanager
from subprocess import Popen
from typing import Any, Dict, IO, Iterator, List

try:
    import click
except ImportError:
    sys.stderr.write('It seems python-dotenv is not installed with cli option. \n'
                     'Run pip install "python-dotenv[cli]" to fix this.')
    sys.exit(1)

from .main import dotenv_values, set_key, unset_key
from .version import __version__


def enumerate_env():
    """
    Return a path for the ${pwd}/.env file.

    If pwd does not exist, return None.
    """
    try:
        cwd = os.getcwd()
    except FileNotFoundError:
        return None
    path = os.path.join(cwd, '.env')
    return path


@click.group()
@click.option('-f', '--file', default=enumerate_env(),
              type=click.Path(file_okay=True),
              help="Location of the .env file, defaults to .env file in current working directory.")
@click.option('-q', '--quote', default='always',
              type=click.Choice(['always', 'never', 'auto']),
              help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.")
@click.option('-e', '--export', default=False,
              type=click.BOOL,
              help="Whether to write the dot file as an executable bash script.")
@click.version_option(version=__version__)
@click.pass_context
def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None:
    """This script is used to set, get or unset values from a .env file."""
    ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file}


@contextmanager
def stream_file(path: os.PathLike) -> Iterator[IO[str]]:
    """
    Open a file and yield the corresponding (decoded) stream.

    Exits with error code 2 if the file cannot be opened.
    """

    try:
        with open(path) as stream:
            yield stream
    except OSError as exc:
        print(f"Error opening env file: {exc}", file=sys.stderr)
        exit(2)


@cli.command()
@click.pass_context
@click.option('--format', default='simple',
              type=click.Choice(['simple', 'json', 'shell', 'export']),
              help="The format in which to display the list. Default format is simple, "
                   "which displays name=value without quotes.")
def list(ctx: click.Context, format: bool) -> None:
    """Display all the stored key/value."""
    file = ctx.obj['FILE']

    with stream_file(file) as stream:
        values = dotenv_values(stream=stream)

    if format == 'json':
        click.echo(json.dumps(values, indent=2, sort_keys=True))
    else:
        prefix = 'export ' if format == 'export' else ''
        for k in sorted(values):
            v = values[k]
            if v is not None:
                if format in ('export', 'shell'):
                    v = shlex.quote(v)
                click.echo(f'{prefix}{k}={v}')


@cli.command()
@click.pass_context
@click.argument('key', required=True)
@click.argument('value', required=True)
def set(ctx: click.Context, key: Any, value: Any) -> None:
    """Store the given key/value."""
    file = ctx.obj['FILE']
    quote = ctx.obj['QUOTE']
    export = ctx.obj['EXPORT']
    success, key, value = set_key(file, key, value, quote, export)
    if success:
        click.echo(f'{key}={value}')
    else:
        exit(1)


@cli.command()
@click.pass_context
@click.argument('key', required=True)
def get(ctx: click.Context, key: Any) -> None:
    """Retrieve the value for the given key."""
    file = ctx.obj['FILE']

    with stream_file(file) as stream:
        values = dotenv_values(stream=stream)

    stored_value = values.get(key)
    if stored_value:
        click.echo(stored_value)
    else:
        exit(1)


@cli.command()
@click.pass_context
@click.argument('key', required=True)
def unset(ctx: click.Context, key: Any) -> None:
    """Removes the given key."""
    file = ctx.obj['FILE']
    quote = ctx.obj['QUOTE']
    success, key = unset_key(file, key, quote)
    if success:
        click.echo(f"Successfully removed {key}")
    else:
        exit(1)


@cli.command(context_settings={'ignore_unknown_options': True})
@click.pass_context
@click.option(
    "--override/--no-override",
    default=True,
    help="Override variables from the environment file with those from the .env file.",
)
@click.argument('commandline', nargs=-1, type=click.UNPROCESSED)
def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
    """Run command with environment variables present."""
    file = ctx.obj['FILE']
    if not os.path.isfile(file):
        raise click.BadParameter(
            f'Invalid value for \'-f\' "{file}" does not exist.',
            ctx=ctx
        )
    dotenv_as_dict = {
        k: v
        for (k, v) in dotenv_values(file).items()
        if v is not None and (override or k not in os.environ)
    }

    if not commandline:
        click.echo('No command given.')
        exit(1)
    ret = run_command(commandline, dotenv_as_dict)
    exit(ret)


def run_command(command: List[str], env: Dict[str, str]) -> int:
    """Run command in sub process.

    Runs the command in a sub process with the variables from `env`
    added in the current environment variables.

    Parameters
    ----------
    command: List[str]
        The command and it's parameters
    env: Dict
        The additional environment variables

    Returns
    -------
    int
        The return code of the command

    """
    # copy the current environment variables and add the vales from
    # `env`
    cmd_env = os.environ.copy()
    cmd_env.update(env)

    p = Popen(command,
              universal_newlines=True,
              bufsize=0,
              shell=False,
              env=cmd_env)
    _, _ = p.communicate()

    return p.returncode

```

### backend/env/lib/python3.12/site-packages/watchfiles/cli.py

```python
import argparse
import logging
import os
import shlex
import sys
from pathlib import Path
from textwrap import dedent
from typing import Any, Callable, List, Optional, Tuple, Union, cast

from . import Change
from .filters import BaseFilter, DefaultFilter, PythonFilter
from .run import detect_target_type, import_string, run_process
from .version import VERSION

logger = logging.getLogger('watchfiles.cli')


def resolve_path(path_str: str) -> Path:
    path = Path(path_str)
    if not path.exists():
        raise FileNotFoundError(path)
    else:
        return path.resolve()


def cli(*args_: str) -> None:
    """
    Watch one or more directories and execute either a shell command or a python function on file changes.

    Example of watching the current directory and calling a python function:

        watchfiles foobar.main

    Example of watching python files in two local directories and calling a shell command:

        watchfiles --filter python 'pytest --lf' src tests

    See https://watchfiles.helpmanual.io/cli/ for more information.
    """
    args = args_ or sys.argv[1:]
    parser = argparse.ArgumentParser(
        prog='watchfiles',
        description=dedent((cli.__doc__ or '').strip('\n')),
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument('target', help='Command or dotted function path to run')
    parser.add_argument(
        'paths', nargs='*', default='.', help='Filesystem paths to watch, defaults to current directory'
    )

    parser.add_argument(
        '--ignore-paths',
        nargs='?',
        type=str,
        help=(
            'Specify directories to ignore, '
            'to ignore multiple paths use a comma as separator, e.g. "env" or "env,node_modules"'
        ),
    )
    parser.add_argument(
        '--target-type',
        nargs='?',
        type=str,
        default='auto',
        choices=['command', 'function', 'auto'],
        help=(
            'Whether the target should be intercepted as a shell command or a python function, '
            'defaults to "auto" which infers the target type from the target string'
        ),
    )
    parser.add_argument(
        '--filter',
        nargs='?',
        type=str,
        default='default',
        help=(
            'Which files to watch, defaults to "default" which uses the "DefaultFilter", '
            '"python" uses the "PythonFilter", "all" uses no filter, '
            'any other value is interpreted as a python function/class path which is imported'
        ),
    )
    parser.add_argument(
        '--args',
        nargs='?',
        type=str,
        help='Arguments to set on sys.argv before calling target function, used only if the target is a function',
    )
    parser.add_argument('--verbose', action='store_true', help='Set log level to "debug", wins over `--verbosity`')
    parser.add_argument(
        '--non-recursive', action='store_true', help='Do not watch for changes in sub-directories recursively'
    )
    parser.add_argument(
        '--verbosity',
        nargs='?',
        type=str,
        default='info',
        choices=['warning', 'info', 'debug'],
        help='Log level, defaults to "info"',
    )
    parser.add_argument(
        '--sigint-timeout',
        nargs='?',
        type=int,
        default=5,
        help='How long to wait for the sigint timeout before sending sigkill.',
    )
    parser.add_argument(
        '--grace-period',
        nargs='?',
        type=float,
        default=0,
        help='Number of seconds after the process is started before watching for changes.',
    )
    parser.add_argument(
        '--sigkill-timeout',
        nargs='?',
        type=int,
        default=1,
        help='How long to wait for the sigkill timeout before issuing a timeout exception.',
    )
    parser.add_argument(
        '--ignore-permission-denied',
        action='store_true',
        help='Ignore permission denied errors while watching files and directories.',
    )
    parser.add_argument('--version', '-V', action='version', version=f'%(prog)s v{VERSION}')
    arg_namespace = parser.parse_args(args)

    if arg_namespace.verbose:
        log_level = logging.DEBUG
    else:
        log_level = getattr(logging, arg_namespace.verbosity.upper())

    hdlr = logging.StreamHandler()
    hdlr.setLevel(log_level)
    hdlr.setFormatter(logging.Formatter(fmt='[%(asctime)s] %(message)s', datefmt='%H:%M:%S'))
    wg_logger = logging.getLogger('watchfiles')
    wg_logger.addHandler(hdlr)
    wg_logger.setLevel(log_level)

    if arg_namespace.target_type == 'auto':
        target_type = detect_target_type(arg_namespace.target)
    else:
        target_type = arg_namespace.target_type

    if target_type == 'function':
        logger.debug('target_type=function, attempting import of "%s"', arg_namespace.target)
        import_exit(arg_namespace.target)
        if arg_namespace.args:
            sys.argv = [arg_namespace.target] + shlex.split(arg_namespace.args)
    elif arg_namespace.args:
        logger.warning('--args is only used when the target is a function')

    try:
        paths = [resolve_path(p) for p in arg_namespace.paths]
    except FileNotFoundError as e:
        print(f'path "{e}" does not exist', file=sys.stderr)
        sys.exit(1)

    watch_filter, watch_filter_str = build_filter(arg_namespace.filter, arg_namespace.ignore_paths)

    logger.info(
        'watchfiles v%s 👀  path=%s target="%s" (%s) filter=%s...',
        VERSION,
        ', '.join(f'"{p}"' for p in paths),
        arg_namespace.target,
        target_type,
        watch_filter_str,
    )

    run_process(
        *paths,
        target=arg_namespace.target,
        target_type=target_type,
        watch_filter=watch_filter,
        debug=log_level == logging.DEBUG,
        sigint_timeout=arg_namespace.sigint_timeout,
        sigkill_timeout=arg_namespace.sigkill_timeout,
        recursive=not arg_namespace.non_recursive,
        ignore_permission_deni
[truncated — 1705 more characters]
```

[3527 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]