# Project export: SlugMeditate

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: CruzHacks 2025
- Tagline: Transforming calm thoughts into immersive VR journeys.
- Devpost: https://devpost.com/software/slugmeditate
- GitHub: https://github.com/briankhoi/slugmeditate
- Demo: https://slugmeditate.tech/
- Result: winner ([Sponsor - Niantic studio WebXR track])
- Team: 4 GitHub contributor(s) — Marcus Leung (10 commits), Brian Khoi Nguyen (10 commits), Mudit Arora (4 commits), bill zhang (4 commits)

## Devpost submission (written by the team)

### Overview

Transforming calm thoughts into immersive VR journeys. SlugMeditate is a calming VR experience that turns your thoughts into immersive worlds. Just type in a peaceful idea, and we use AI to generate visuals, turn them into video, and transform it into a 3D scene you can explore in virtual reality — all in your browser. It’s like a personalized meditation space, built from your own imagination. 🌟

### Inspiration

We wanted to explore how emerging generative AI and VR technologies could support mindfulness and mental well-being. Many college students deal with stress and burnout — what if we could turn a simple reflection or intention into a deeply personal, calming VR experience? Thus, SlugMeditate was born: a pipeline that transforms your thoughts into a 3D ambient escape. ✨ What It Does SlugMeditate takes a text input (like "a peaceful forest at dusk") and transforms it into: Image Generation – Using Google Imagen 3, powered by Google AI Gemini, we generate visually rich and vivid images from the user’s prompt. Video Creation – These images are animated into smooth, cinematic video loops using Google AI Gemini Veo 2. 3D Scene Mapping – The generated video is converted into a volumetric 3D scene using Gaussian Splatting. VR Immersion – We render the 3D environment in-browser using Niantic Studio WebXR, paired with ambient music for a fully immersive and relaxing VR experience. Ambient Music Generation – Using Google MusicFX, we generate peaceful background music tailored to your prompt, completing the meditative vibe. 🧠 How We Built It Text-to-Image: Generated with Imagen 3, part of Google AI Gemini's generative capabilities. Image-to-Video: Animated using Gemini AI Veo 2 for cinematic smoothness and style. 3D Reconstruction: Used Gaussian Splatting to map video into a navigable 3D environment. VR Deployment: Built and displayed the immersive world using Niantic Studio WebXR. Sound Design: Integrated calming ambient tracks to enhance the meditative experience. 🧗 Challenges We Ran Into A lot of the API documentation for Google tools (like Imagen 3 and Veo 2) was either outdated or didn’t work as expected, so we had to piece things together from multiple sources and experiment through trial and error. We tried multiple methods of Gaussian Splatting, but hit limitations due to hardware constraints and time. It was tricky to get the image-to-video prompting just right. If the camera motion didn’t pan correctly or stayed too static, the 3D reconstruction would break or look flat. We were all new to Niantic Studio WebXR, so learning the ecosystem and figuring out how to display our 3D scene in a browser-based VR environment took some digging. CruzHacks had unreliable Wi-Fi, which made working with cloud-based AI tools extra tough — we had to sync and generate assets whenever the connection let us. 🏆 Accomplishments We're Proud Of Built a complete text-to-VR pipeline using cutting-edge generative AI in under 48 hours. Successfully integrated Google AI Gemini tools with VR rendering frameworks. Created an emotionally resonant experience combining creativity, tech, and wellness. Learned and operationalized advanced tools like Imagen 3, Veo 2, and Gaussian Splatting quickly. 📚 What We Learned Practical use of Google AI Gemini for generative media workflows. How to bridge AI media generation with 3D spatial computing and VR rendering. Rapid prototyping, team collaboration, and problem-solving under pressure. 🔮 What's Next Interactive environments – Let users explore within the VR space. Customization – Personalize music, ambient sounds, or guided meditations. Performance tuning – Reduce generation and render time for real-time feedback. Community gallery – Share and explore others' meditative scenes. Built with love, curiosity, and caffeine by the SlugMeditate team at CruzHacks 2025 Powered by Google AI Gemini, Niantic Studio WebXR, and Gaussian Splatting.

## README (from the GitHub repository)

# 🐌 SlugMeditate

*Transforming calm thoughts into immersive VR journeys.*

SlugMeditate is a calming VR experience that turns your thoughts into immersive worlds. Just type in a peaceful idea, and we use AI to generate visuals, turn them into video, and transform it into a 3D scene you can explore in virtual reality — all in your browser. It’s like a personalized meditation space, built from your own imagination.

## 🌟 Inspiration

We wanted to explore how emerging generative AI and VR technologies could support mindfulness and mental well-being. Many college students deal with stress and burnout — what if we could turn a simple reflection or intention into a deeply personal, calming VR experience?

Thus, **SlugMeditate** was born: a pipeline that transforms your thoughts into a 3D ambient escape.

## ✨ What It Does

SlugMeditate takes a **text input** (like _"a peaceful forest at dusk"_) and transforms it into:

1. **Image Generation** – Using **Google Imagen 3**, powered by **Google AI Gemini**, we generate visually rich and vivid images from the user’s prompt.
2. **Video Creation** – These images are animated into smooth, cinematic video loops using **Google AI Gemini Veo 2**.
3. **3D Scene Mapping** – The generated video is converted into a volumetric 3D scene using **Gaussian Splatting**.
4. **VR Immersion** – We render the 3D environment in-browser using **Niantic Studio WebXR**, paired with ambient music for a fully immersive and relaxing VR experience.
5. **Ambient Music Generation** – Using **Google MusicFX**, we generate peaceful background music tailored to your prompt, completing the meditative vibe.

## 🧠 How We Built It

- **Text-to-Image**: Generated with **Imagen 3**, part of **Google AI Gemini**'s generative capabilities.
- **Image-to-Video**: Animated using **Gemini AI Veo 2** for cinematic smoothness and style.
- **3D Reconstruction**: Used **Gaussian Splatting** to map video into a navigable 3D environment.
	- Trained an AI 3D Gaussian Splatting model locally on our laptop GPUs implementing the paper "3D Gaussian Splatting for Real-Time Radiance Field Rendering
": https://github.com/graphdeco-inria/gaussian-splatting
- **VR Deployment**: Built and displayed the immersive world using **Niantic Studio WebXR**.
- **Sound Design**: Integrated calming ambient tracks to enhance the meditative experience.

![Pipline](pipeline.png)

## 🧗 Challenges We Ran Into

- A lot of the **API documentation for Google tools** (like Imagen 3 and Veo 2) was either outdated or didn’t work as expected, so we had to piece things together from multiple sources and experiment through trial and error.
- We tried multiple methods of **Gaussian Splatting**, but hit limitations due to **hardware constraints** and time.
- It was tricky to get the **image-to-video prompting** just right. If the camera motion didn’t pan correctly or stayed too static, the 3D reconstruction would break or look flat.
- We were all **new to Niantic Studio WebXR**, so learning the ecosystem and figuring out how to display our 3D scene in a browser-based VR environment took some digging.
- **CruzHacks had unreliable Wi-Fi**, which made working with cloud-based AI tools extra tough — we had to sync and generate assets whenever the connection let us.

## 🏆 Accomplishments We're Proud Of

- Built a complete **text-to-VR pipeline** using cutting-edge generative AI in under 48 hours.
- Successfully integrated **Google AI Gemini** tools with VR rendering frameworks.
- Created an emotionally resonant experience combining creativity, tech, and wellness.
- Learned and operationalized advanced tools like **Imagen 3**, **Veo 2**, and **Gaussian Splatting** quickly.

## 📚 What We Learned

- Practical use of **Google AI Gemini** for generative media workflows.
- How to bridge AI media generation with 3D spatial computing and VR rendering.
- Rapid prototyping, team collaboration, and problem-solving under pressure.

## 🔮 What's Next

- **Interactive environments** – Let users explore within the VR space.
- **Customization** – Personalize music, ambient sounds, or guided meditations.
- **Performance tuning** – Reduce generation and render time for real-time feedback.
- **Community gallery** – Share and explore others' meditative scenes.

---

> Built with love, curiosity, and caffeine by the **SlugMeditate** team at CruzHacks 2025   
> Powered by **Google AI Gemini**, **Niantic Studio WebXR**, and **Gaussian Splatting**.

Docker Setup:
1. Run `docker build -t python-env .`
2. docker run -it --rm -v "$(your path to directory):/app" python-env
	- for mac: docker run -it --rm -v "$PWD:/app" python-env
	- windows: docker run -it --rm -v "/$(pwd | sed 's/^C:\\//; s/\\/\//g'):/app" python-env


## Detected evidence (automated analysis)

Indexed codebase: 19 recognized source files, 59 KB.
- HTML (language) — detected in the code
- Python (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- Next.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (34 of 34)

```
.gitignore
convert.mjs
data_data_gen_two/distorted/database.db-shm
data_data_gen_two/distorted/database.db-wal
data_gen_two/run-colmap-geometric.sh
data_gen_two/run-colmap-photometric.sh
data_gen_two/stereo/fusion.cfg
data_gen_two/stereo/patch-match.cfg
Dockerfile
gaussian_splat.sh
image_to_video.ipynb
image_to_video.py
imagetovideo.ipynb
model_gen_two/cameras.json
model_gen_two/cfg_args
model_gen_two/exposure.json
Music-Gen/app.py
Music-Gen/main.py
Music-Gen/requirements.txt
Music-Gen/src/config/settings.py
Music-Gen/src/models/audio_model.py
Music-Gen/src/services/api_client.py
Music-Gen/src/utils/file_utils.py
Music-Gen/src/utils/rewrite_prompt.py
Music-Gen/src/utils/validation_utils.py
Music-Gen/templates/index.html
package.json
plt_to_glb.py
plt_to_obj.py
prompt_to_image.ipynb
README.md
requirements.txt
test_color_save.py
text_to_image.py
```

### Dependencies

- package.json: spz-js@^1.2.2
- requirements.txt: certifi@==2025.1.31, charset-normalizer@==3.4.1, colorama@==0.4.6, google-genai@==1.10.0, idna@==3.10, iniconfig@==2.0.0, ipython@==9.1.0, packaging@==24.2, Pillow@==11.2.1, pluggy@==1.5.0, pytest@==8.3.4, python-dotenv@==1.1.0, requests@==2.32.3, urllib3@==2.3.0

### Recent commits (newest first)

- Update README.md
- Merge branch 'main' of https://github.com/briankhoi/slugmeditate
- add pipeline img
- merge
- Update README.md
- Notebooks
- Gitignore
- plt to spz
- gaussian_splat
- pure python main file
- use flask
- Merge branch 'main' of https://github.com/briankhoi/slugmeditate
- html file
- remove files
- Update image_to_video.py
- Merge branch 'main' of https://github.com/briankhoi/slugmeditate
- Update imagetovideo.ipynb
- updated docker readme
- merge
- added dockerfile

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

### package.json

```
{
	"dependencies": {
		"spz-js": "^1.2.2"
	}
}

```

### requirements.txt

```
google-genai==1.10.0
Pillow==11.2.1
python-dotenv==1.1.0
ipython==9.1.0
certifi==2025.1.31
charset-normalizer==3.4.1
colorama==0.4.6
idna==3.10
iniconfig==2.0.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.4
requests==2.32.3
urllib3==2.3.0

```

### Dockerfile

```
# Use Python 3.12 base image
FROM python:3.12-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    libffi-dev \
    libjpeg-dev \
    libpng-dev \
    && rm -rf /var/lib/apt/lists/*

# Create working directory
WORKDIR /app

# Copy only requirements to install deps
COPY . .

# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Default command (just opens shell)
CMD ["bash"]

```

### Music-Gen/requirements.txt

```
��c e r t i f i = = 2 0 2 5 . 1 . 3 1  
 c h a r s e t - n o r m a l i z e r = = 3 . 4 . 1  
 c o l o r a m a = = 0 . 4 . 6  
 i d n a = = 3 . 1 0  
 i n i c o n f i g = = 2 . 0 . 0  
 p a c k a g i n g = = 2 4 . 2  
 p l u g g y = = 1 . 5 . 0  
 p y t e s t = = 8 . 3 . 4  
 p y t h o n - d o t e n v = = 1 . 0 . 1  
 r e q u e s t s = = 2 . 3 2 . 3  
 u r l l i b 3 = = 2 . 3 . 0  
 
```

### Music-Gen/app.py

```python
from src.services.api_client import APIClient
from src.utils.file_utils import generate_filename, save_audio_file
from src.utils.validation_utils import validate_music_prompt
from src.utils.rewrite_prompt import rewrite_music_prompt, record_audio_from_mic, transcribe_speech_to_text

def main():
    """
    Main function to run the Google Music FX application.
    Prompts the user for input, generates music, and saves the audio files.
    
    Handles exceptions and provides user feedback.
    """
    try:
        api_client = APIClient()
        input_mode = input("Would you like to [t]ype or [s]peak your prompt? (t/s): ").strip().lower()

        if input_mode == 's':
            record_audio_from_mic()
            user_input = transcribe_speech_to_text()
            print(f"Transcribed text: {user_input}")
        else:
            user_input = input("Please enter how you're feeling or what vibe you want the music to reflect: ")

        music_prompt = rewrite_music_prompt(user_input)
        print("\nEnhanced music prompt:\n", music_prompt)
        validate_music_prompt(music_prompt)
        
        sounds = api_client.generate_music(music_prompt)
        
        for idx, sound in enumerate(sounds, start=1):
            file_name = f"static/{generate_filename('generated_music')}"
            save_audio_file(sound["data"], file_name, sound["audioContainer"].lower())
            
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    main()
```

### Music-Gen/main.py

```python
from flask import Flask, render_template, request, jsonify
from src.services.api_client import APIClient
from src.utils.validation_utils import validate_music_prompt
from src.utils.rewrite_prompt import rewrite_music_prompt, record_audio_from_mic, transcribe_speech_to_text
from src.utils.file_utils import save_audio_file, generate_filename

app = Flask(__name__, static_folder='static', template_folder='templates')

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/generate-music', methods=['POST'])
def generate_music():
    data = request.json
    mode = data.get('mode', 'text')

    try:
        if mode == 'speech':
            record_audio_from_mic()
            user_input = transcribe_speech_to_text()
        else:
            user_input = data.get('prompt')

        if not user_input:
            return jsonify({"error": "Prompt is required"}), 400

        music_prompt = rewrite_music_prompt(user_input)
        validate_music_prompt(music_prompt)

        api_client = APIClient()
        sounds = api_client.generate_music(music_prompt)

        urls = []
        for idx, sound in enumerate(sounds, start=1):
            file_name = f"static/{generate_filename('generated_music')}"
            save_audio_file(sound["data"], file_name, sound["audioContainer"].lower())
            urls.append(f"/{file_name}")

        return jsonify({"music_urls": urls})

    except Exception as e:
        return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)

```

### gaussian_splat.sh

```shell
#!/bin/bash

# Check if filename argument is provided
if [ $# -eq 0 ]; then
    echo "Please provide an input video file"
    echo "Usage: $0 <video_file> [fps]"
    exit 1
fi

INPUT_FILE=$1
# Set default FPS if not provided
FPS=${2:-2}
# Extract filename without extension
FILENAME=$(basename "$INPUT_FILE" | sed 's/\.[^.]*$//')

# Create unique directories for this run
DATA_DIR="data_${FILENAME}"
MODEL_DIR="model_${FILENAME}"

rm -rf "${DATA_DIR}/input"
rm -rf "${MODEL_DIR}"
mkdir -p "${DATA_DIR}/input"

ffmpeg -i "$INPUT_FILE" -vf fps=${FPS} "${DATA_DIR}/input/frame_%04d.png"

python gaussian-splatting/convert.py -s "${DATA_DIR}"

python gaussian-splatting/train.py -s "${DATA_DIR}" -m "${MODEL_DIR}" --iterations 7000

tar -czvf "${MODEL_DIR}.tar.gz" "${MODEL_DIR}"

```

### test_color_save.py

```python
import open3d as o3d
import numpy as np

print(f"Testing simple mesh save with Open3D version: {o3d.__version__}")

# Create a simple cube mesh
mesh = o3d.geometry.TriangleMesh.create_box()
mesh.compute_vertex_normals()

# Assign vertex colors (based on position)
vertices = np.asarray(mesh.vertices)
# Normalize position to range [0, 1] for color
min_b = vertices.min(axis=0)
max_b = vertices.max(axis=0)
if (max_b - min_b).any() != 0 : # Avoid division by zero if box has zero size in any dim
    colors = (vertices - min_b) / (max_b - min_b)
else:
    colors = np.zeros_like(vertices) # Default to black if size is zero
    colors[:,0] = 1.0 # Make it red instead of black just to be visible

mesh.vertex_colors = o3d.utility.Vector3dVector(colors)
print("Assigned vertex colors to simple cube.")
print("Mesh has vertex colors flag:", mesh.has_vertex_colors())

# --- SAVE AS OBJ ---
output_simple_path_obj = "output_TEST_simple_cube_colors.obj"
print(f"Saving simple cube with vertex colors to {output_simple_path_obj}...")
success_obj = o3d.io.write_triangle_mesh(output_simple_path_obj, mesh, write_vertex_colors=True) # OBJ export

if success_obj:
    print("Simple cube saved successfully as OBJ.")
    print("Please view this OBJ file in MeshLab or Blender.")
else:
    print("ERROR: Failed to save the simple cube file as OBJ!")

# --- Optional: Also save as GLB for comparison ---
output_simple_path_glb = "output_TEST_simple_cube_colors.glb"
print(f"Saving simple cube with vertex colors to {output_simple_path_glb}...")
success_glb = o3d.io.write_triangle_mesh(output_simple_path_glb, mesh, write_vertex_colors=True) # GLB export
if not success_glb:
     print("ERROR: Failed to save the simple cube file as GLB!")
```

### image_to_video.py

```python
import time
import os
from dotenv import load_dotenv
from google import genai
from google.genai import types
from IPython import display

# Import functions from text-to-image.py
# Assuming the refactored file is named text_to_image.py (with underscore to follow Python conventions)
from text_to_image import generate_text_prompt, enhance_prompt, generate_image, save_and_display_image

load_dotenv()

def generate_video_from_image(client, image_response):
    """
    Generates videos from an image using Google's Veo model.
    
    Args:
        client: Google Generative AI client
        image_response: Response from Imagen image generation
    
    Returns:
        List of filenames of saved videos
    """
    # Extract the image from the image_response
    if not hasattr(image_response, 'generated_images') or not image_response.generated_images:
        print("No image available to generate video from")
        return []
    
    generated_image = image_response.generated_images[0]
    
    if not hasattr(generated_image, 'image') or not hasattr(generated_image.image, 'image_bytes'):
        print("Image object does not have expected attributes")
        return []
    
    # Initialize operation for video generation
    operation = client.models.generate_videos(
        model="veo-2.0-generate-001",
        prompt="a side to side shot in the center of a temple of a cozy vibrant warm relaxing zen temple with no humans or animals in it",
        image=generated_image.image,  # Pass the generated image object directly
        config=types.GenerateVideosConfig(
            aspect_ratio="16:9",  # "16:9" or "9:16"
            number_of_videos=1,
        ),
    )
    
    print("Video generation initiated. Waiting for completion...")
    
    # Wait for videos to generate
    while not operation.done:
        time.sleep(20)
        operation = client.operations.get(operation)

    for n, generated_video in enumerate(operation.result.generated_videos):
        client.files.download(file=generated_video.video)
        generated_video.video.save(f'video{n}.mp4') # Saves the video(s)
    
    return None

def main():
    # User prompt for image generation
    user_prompt = "a side to side shot in the center of a temple open area of a cozy vibrant warm relaxing zen temple with no humans or animals in it"
    
    # Initialize client
    client = genai.Client(api_key=os.getenv("API_KEY"))
    
    # Generate text prompt
    print("Generating text prompt...")
    text_prompt = generate_text_prompt(client, user_prompt)
    
    if text_prompt:
        print("Text prompt generated successfully.")
        
        # Enhance the prompt
        image_prompt = enhance_prompt(text_prompt)
        print("Enhanced prompt for image generation.")
        
        # Generate image
        print("Generating image...")
        image_response = generate_image(client, image_prompt)
        
        if image_response:
            print("Image generated successfully.")
            
            # Save the image (optional)
            save_and_display_image(image_response)
            
            # Generate videos from the image
            print("Starting video generation process...")
            generate_video_from_image(client, image_response)
            
            # if video_files:
            #     print(f"Video generation complete. Generated {len(video_files)} videos:")
            #     for file in video_files:
            #         print(f"- {file}")
            # else:
            #     print("Video generation failed or produced no videos.")
        else:
            print("Failed to generate image. Cannot proceed to video generation.")
    else:
        print("Failed to generate text prompt. Process aborted.")

if __name__ == "__main__":
    main()
```

### text_to_image.py

```python
from google import genai
from google.genai import types
from PIL import Image
from io import BytesIO
import random
from IPython.display import display
import os
from dotenv import load_dotenv

load_dotenv()

def enhance_prompt(user_prompt):
    """Enhances the user's prompt for better image generation results."""
    keywords_3d = ["3D render", "octane render", "ray traced", "highly detailed", "8k resolution"]
    keywords_cozy = ["warm lighting", "soft textures", "comfortable"]
    keywords_meditative = ["serene", "peaceful", "tranquil", "calm", "minimalism", "natural light", "meditation", "zen"]

    num_keywords = 3

    enhanced_prompt = user_prompt + ", "
    enhanced_prompt += ", ".join(random.sample(keywords_3d, min(num_keywords, len(keywords_3d)))) + ", "
    enhanced_prompt += ", ".join(random.sample(keywords_cozy, min(num_keywords, len(keywords_cozy)))) + ", "
    enhanced_prompt += ", ".join(random.sample(keywords_meditative, min(num_keywords, len(keywords_meditative)))) + ", "
    return enhanced_prompt

def generate_text_prompt(client, user_prompt):
    """Generates an enhanced text prompt using Gemini."""
    try:
        response = client.models.generate_content(
            model="gemini-2.0-flash",
            contents=[f"REQUIRED: THE POV/CAMERA IS SITTING IN THE MIDDLE OF A LANDSCAPE WITH 90 DEGREES FIELD OF VIEW. NOT AN OVERHEAD SHOT OF THE SCENE BUT THE PERSPECTIVE OF SITTING DIRECTLY IN THE SCENE 90 DEGREES FIELD OF VIEW. DO NOT INCLUDE ANY PEOPLE INSIDE OF THE IMAGE. do not include asterisks in your prompt or any colons, just normal characters. Refine and enhance the following user-provided scene description to create a highly detailed and evocative prompt suitable for a state-of-the-art image generation AI like Imagen 3. Make the middle of the scene a vast, open relaxing area where a person could sit and meditate BUT DONT PLACE A PERSON THERE. REQUIRED: The scene should be the PERSPECTIVE of a person sitting in the middle with a 90 degree field of view. Focus on elements that will create a rich and immersive meditative experience. Consider details related to lighting, atmosphere, sensory details (visual, auditory, and even subtle tactile or olfactory hints if appropriate), artistic style, and overall mood. Ensure the prompt is specific and avoids ambiguity. DO NOT GIVE ME ANYTHING ELSE, JUST THE PROMPT TO FEED INTO THE IMAGE MODEL, DO NOT SAY OK HERE'S YOUR PROMPT OR WHY ITS EFFECTIVE OR  CONSIDERATIONS JUST GIVE ME THE PROMPT TO FEED INTO THE OTHER MODEL. \nUser's Initial Scene: {user_prompt}"]
        )
        return response.text
    except Exception as e:
        print(f"Error generating text prompt: {e}")
        return None

def generate_image(client, image_prompt):
    """Generates an image using Imagen and returns the response."""
    try:
        image_response = client.models.generate_images(
            model='imagen-3.0-generate-002',
            prompt=image_prompt,
            config=types.GenerateImagesConfig(
                number_of_images=1,
                aspect_ratio='16:9',
                personGeneration="DONT_ALLOW",
            )
        )
        return image_response
    except Exception as e:
        print(f"Error generating image: {e}")
        import traceback
        traceback.print_exc()
        return None

def save_and_display_image(image_response, filename="generated_image.png"):
    """Saves and displays the generated image."""
    if hasattr(image_response, 'generated_images') and image_response.generated_images:
        print(f"Number of generated images: {len(image_response.generated_images)}")
        
        generated_image = image_response.generated_images[0]
        
        if hasattr(generated_image, 'image') and hasattr(generated_image.image, 'image_bytes'):
            image_bytes = generated_image.image.image_bytes
            image = Image.open(BytesIO(image_bytes))
            image.save(filename)
            print(f"Image saved successfully as '{filename}'")
            display(Image.open(filename))
            image.show()
            return True
        else:
            print("Image object does not have expected 'image' or 'image_bytes' attributes")
    else:
        print("No generated images found in response")
        print("Full response:", image_response)
    return False

def main():
    user_prompt = "a side to side shot in the open area center of a temple of a cozy vibrant warm relaxing zen temple with no humans or animals in it"
    
    # Initialize client
    client = genai.Client(api_key=os.getenv("API_KEY"))
    
    # Generate text prompt
    text_prompt = generate_text_prompt(client, user_prompt)
    if text_prompt:
        print("Text prompt generated successfully:")
        print(text_prompt)
        
        # Enhance the prompt
        image_prompt = enhance_prompt(text_prompt)
        print("\nFinal image prompt:")
        print(image_prompt)
        
        # Generate image
        print("\nGenerating image...")
        image_response = generate_image(client, image_prompt)
        
        if image_response:
            # Debug information
            print(f"Response type: {type(image_response)}")
            print(f"Response attributes: {dir(image_response)}")
            
            # Save and display the image
            save_and_display_image(image_response)

if __name__ == "__main__":
    main()
```

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