DEV Community

Cover image for Building a Voice-First AI Reflection Journal with 3D Mindset Constellations on Google Cloud Run
Sagar Maurya
Sagar Maurya

Posted on

Building a Voice-First AI Reflection Journal with 3D Mindset Constellations on Google Cloud Run

Most digital journals are fundamentally passive text storage. You type your thoughts into an empty text area, save the entry under a timestamp, and close the tab. Over time, that leaves you with an unindexed graveyard of text entries that are rarely revisited, organized, or connected.

When developers attempt to introduce AI into journaling tools, the implementation often stops at generic chatbot wrappers or ungrounded motivational slogans. What is missing is structure, cognitive continuity, and active reflection:

  1. Low-barrier capture: When you are fatigued after hours of engineering, sitting down to type several paragraphs creates friction. Speaking aloud with natural voice dictation lowers the barrier to getting thoughts out of your head.
  2. Socratic feedback: Instead of generic positive reinforcement, a reflective partner should identify underlying assumptions, clarify trade-offs, and ask thoughtful follow-up questions.
  3. Cross-session memory stitching: Daily reflections shouldn't exist in silos. An intelligent journal should identify connections between today's friction and dilemmas you faced three weeks ago.
  4. Spatial pattern synthesis: Rather than keeping thoughts locked in a flat chronological feed, visualizing recurring cognitive themesβ€”breakthroughs, friction, growth, and decisionsβ€”as an interactive 3D universe makes mental habits immediately visible.

To explore this, I built Gemini Reflection Journal: an open, voice-first reflection application powered by Gemini 3.6 Flash, an interactive Three.js WebGL 3D constellation galaxy, and a unified full-stack architecture deployed to Google Cloud Run.

Here is what the interface looks like in practice:

Gemini Reflection Journal Dashboard
Figure 1: The distraction-free reflection workspace featuring live voice dictation, reflection spark selection, Socratic multi-turn dialogue, and historical callbacks.


πŸ—οΈ System Architecture & Data Flow

The application is structured as a unified full-stack service where a React single-page frontend and an Express API proxy are served from a single container:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                               React 18 + Vite Frontend                                 β”‚
β”‚       (Tailwind CSS β€’ Lucide Icons β€’ Web Speech API β€’ Three.js 3D Cosmic Galaxy)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚                                β”‚
                   (Firebase Auth SDK)               (Internal API Proxy)
                   (Owner-Bound Firestore Sync)      (/api/chat, /api/summarize,
                            β”‚                         /api/socratic-callback,
                            β”‚                         /api/mindset-constellation)
                            β–Ό                                β”‚
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β–Ό
               β”‚ Google Cloud Firestore β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚ (Data Isolation Rules) β”‚      β”‚ Node/Express on Cloud Run   β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚ (GCP Secret Manager Ingest) β”‚
                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                              β”‚
                                                   (@google/genai SDK)
                                                              β–Ό
                                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                               β”‚ Gemini Model Fallback Ladderβ”‚
                                               β”‚ 1. gemini-3.6-flash         β”‚
                                               β”‚ 2. gemini-3.1-flash-lite    β”‚
                                               β”‚ 3. gemini-flash-latest      β”‚
                                               β”‚ 4. gemini-3.8-flash         β”‚
                                               β”‚ 5. gemini-3.7-flash         β”‚
                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Stack Components:

  • Frontend: React 18, TypeScript, Tailwind CSS, Three.js, Lucide Icons, and the browser Web Speech API.
  • Backend: Node.js and Express, bundled into a single self-contained CommonJS artifact (dist/server.cjs) via esbuild.
  • AI Engine: @google/genai TypeScript SDK with an automated circuit-breaker model fallback ladder.
  • Database & Identity: Google Cloud Firestore with owner-bound rules, plus Firebase Authentication (Google Sign-In with an automatic fallback to isolated guest sessions).
  • Secrets Management: Google Cloud Secret Manager.
  • Hosting & Runtime: Google Cloud Run.

🌟 Core System Capabilities

1. Hands-Free Voice Dictation & Spoken Feedback

Typing on a keyboard can interrupt quick trains of thought. The application integrates the browser's native Web Speech API for real-time speech recognition:

  • Continuous Transcription: Captures spoken thoughts and writes directly to the reflection buffer with silence handling.
  • Visual Audio Feedback: Animated speech indicators provide immediate confirmation that your microphone is capturing input without lag.
  • Text-to-Speech Playback: Follow-up questions generated by Gemini can be read aloud using SpeechSynthesis with synchronized play, pause, and stop controls.

Voice Dictation
Audio Dialogue
Figure 2: Real-time voice dictation with animated speech waveform and audio playback of Socratic inquiries.


2. Socratic Reflection Engine

When a reflection is submitted, the backend doesn't output generic advice. The system prompt instructs Gemini to act as a thoughtful Socratic partner:

  • Identifies core tensions, cognitive dissonance, or hidden assumptions.
  • Formulates one or two focused clarifying questions.
  • Suggests small, concrete next actions rather than abstract platitudes.

Users can converse across multiple turns, digging deeper into a technical problem, team friction, or personal decision before concluding the session.


3. Socratic Callbacks & Cross-Session Memory Stitching

Most journaling applications treat each entry as an isolated event. Our Socratic Callback Engine (POST /api/socratic-callback) queries historical entries to bridge the gap between past and present:

  • Analyzes previous reflections (e.g. from 3, 7, or 14 days ago) alongside today's entry.
  • Detects recurring themes, unresolved tensions, and cognitive growth.
  • Surfaces tailored callbacks: "Three days ago, you noted feeling blocked by asynchronous review delays. In today's entry, you mentioned shipping the auth service. Did your strategy of small PRs resolve the friction, or did team dynamics shift?"

This transforms passive logging into a continuous, active feedback loop.


4. Interactive 3D Mindset Constellations (Three.js WebGL)

The centerpiece of the application is the 3D Mindset Constellation (POST /api/mindset-constellation).

When reflections are recorded, the backend prompts Gemini 3.6 Flash to analyze the text and extract cognitive nodes classified into five primary categories:

  • πŸ’‘ Breakthroughs (moments of sudden clarity or insight)
  • 🌱 Growth (positive momentum, new habits, skill acquisition)
  • ⚑ Friction (blockers, fatigue, technical hurdles)
  • 🧭 Decision-Making (trade-offs, architectural forks, prioritization)
  • 🧘 Mindset (grounding, self-awareness, perspective shifts)

These nodes and their conceptual links are mapped into a custom Three.js WebGL scene:

3D Mindset Constellation Galaxy
Figure 3: Interactive Three.js WebGL 3D universe mapping recurring breakthroughs, friction points, and cognitive growth anchors.

πŸš€ Technical Breakthrough: Zero-Lag Hardware-Accelerated 2D HUD Projection

In hybrid 3D WebGL applications, displaying 2D HTML labels over 3D coordinates often suffers from noticeable lag or jitter. If you pipe 3D coordinates through React state (useState), React's asynchronous render batching creates a 1–2 frame delay behind the canvas. Furthermore, CSS transitions on left/top cause labels to drag like a trailing tail during rotations.

To achieve fluid, 60 FPS synchronization, we engineered a direct DOM projection pipeline:

  1. Direct DOM Refs: Badges are mounted with standard React markup, but their coordinates are managed outside the virtual DOM via badgeElementsRef.
  2. Synchronized Animation Loop: Inside requestAnimationFrame, right after renderer.render(scene, camera), coordinates are calculated via tempV.project(cam) and immediately applied to element styles using hardware-accelerated GPU transforms:
   el.style.transform = `translate3d(${Math.round(screenX)}px, ${Math.round(screenY + offsetY)}px, 0) translate(-50%, ${translateY})`;
Enter fullscreen mode Exit fullscreen mode
  1. Camera Forward Vector Dot-Product Check: Prevents inverted projections when nodes rotate behind the camera lens:
   const toNode = tempV.sub(camPos);
   if (toNode.dot(camDir) <= 0) {
     el.style.display = 'none';
     return;
   }
Enter fullscreen mode Exit fullscreen mode
  1. Dynamic Boundary Flip Logic: When a star orbits near the top edge of the canvas (screenY < 54), the badge automatically flips below the star with an upward-pointing anchor stem, eliminating clipping.
  2. Depth-Based Z-Indexing: Badges dynamically scale their z-index based on normalized camera distance so foreground stars naturally occlude background labels.
  3. Smooth Camera Fly-To: Clicking or searching any star triggers a spherical camera flight directly to the node, opening an inspection drawer with related reflection excerpts.

5. Structured Synthesis & Daily Sparks

  • Executive Synthesis: Users can generate a clean summary of their journal entry containing an executive summary, key realizations, mood/energy tags, and micro-commitments (POST /api/summarize).
  • Daily Reflection Sparks: A built-in prompt generator offers targeted starters across five disciplines: Stoic Premeditatio Malorum, Evening Wind-Down Review, Decision Framing, Mindfulness, and Creative Problem Solving (POST /api/daily-prompt).

Executive Synthesis and Insights
Figure 4: Automated reflection synthesis generating concise executive summaries, sentiment tags, and actionable micro-commitments.


πŸ›‘οΈ Security Architecture & Threat Model

Personal reflections require strict data privacy and isolation. Before building the application logic, we mapped the system against five primary threat zones:

Threat Summary Table

Threat Zone Identified Risk Countermeasure Implemented
Input Surfaces Malicious injection or oversized payload structures in API endpoints. Strict schema validation, top-level JSON request deserialization, and defensive null-safe parameter guarding ((req.body && typeof req.body === 'object') ? req.body : {}).
Planning & Reasoning Prompt injection attempting to divert the Socratic persona. Strict system instruction boundaries; incoming reflections are treated strictly as plain text data, never executable instructions.
Tool & Server Execution API credential exposure or client-side token leakage. Complete backend API proxy architecture. The browser client never touches or stores the Gemini API key.
Memory & Database Cross-user data access or unauthorized document read/write. Owner-bound Firestore security rules verify request.auth.uid == userId on every path and sub-collection.
Secret Management Hardcoded secrets or keys leaked through version control. GEMINI_API_KEY is loaded at container runtime from Google Cloud Secret Manager. Zero secrets exist in code or repository commits.

Owner-Bound Firestore Security Rules

To enforce complete user data isolation at the database level, firestore.rules enforces that only the authenticated user can access their own document trees:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;

      match /interactions/{interactionId} {
        allow read, write: if request.auth != null && request.auth.uid == userId;
      }

      match /entries/{entryId} {
        allow read, write: if request.auth != null && request.auth.uid == userId;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Even if client-side configuration parameters are inspected in the browser, Firestore directly rejects any query where request.auth.uid fails to match the targeted document path.


⚑ Engineering Highlight: Resilient Gemini Model Fallback Ladder

In production, external API endpoints can occasionally return transient 429 Resource Exhausted, 503 Service Unavailable, or capacity errors.

Rather than letting an API hiccup interrupt a user's train of thought, our backend helper wraps @google/genai in an automated multi-tier fallback ladder with circuit-breaker health tracking:

// server.ts - Resilient Gemini Call with Circuit-Breaker Fallback Ladder
import { GoogleGenAI } from '@google/genai';

const BASE_MODEL_LADDER = [
  'gemini-3.6-flash',       // Primary: Low latency, high reasoning quality
  'gemini-3.1-flash-lite',  // High-availability lightweight fallback
  'gemini-flash-latest',    // Dynamic alias fallback
  'gemini-3.8-flash',       // Next-gen reasoning engine
  'gemini-3.7-flash'        // Deep reasoning fallback
];

// Track degraded models to avoid hammering exhausted quotas
const modelDegradedUntil = new Map<string, number>();

function getPrioritizedModelList(): string[] {
  const now = Date.now();
  const healthy: string[] = [];
  const degraded: string[] = [];

  for (const model of BASE_MODEL_LADDER) {
    const degradedUntil = modelDegradedUntil.get(model) || 0;
    if (now < degradedUntil) {
      degraded.push(model);
    } else {
      healthy.push(model);
    }
  }
  return [...healthy, ...degraded];
}

export async function generateContentWithFallback(params: {
  contents: any;
  systemInstruction?: string;
  config?: any;
}) {
  const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
  const candidateModels = getPrioritizedModelList();
  let lastError: any = null;

  for (const model of candidateModels) {
    const isQuota = modelDegradedUntil.get(model) && 
      (modelDegradedUntil.get(model)! - Date.now() > 5 * 60 * 1000);
    if (isQuota) continue; // Skip models with active multi-hour quota exhaustion

    const maxAttempts = 2; // Allow 1 quick retry for transient demand surges
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      try {
        const response = await ai.models.generateContent({
          model,
          contents: params.contents,
          config: {
            systemInstruction: params.systemInstruction,
            ...params.config
          }
        });

        // Success: clear degraded state
        modelDegradedUntil.delete(model);
        return { response, modelUsed: model };
      } catch (err: any) {
        lastError = err;
        const status = err.status || err.statusCode || err.error?.code;

        if (status === 429) {
          // Quota exhausted: cool down model for 15 minutes
          modelDegradedUntil.set(model, Date.now() + 15 * 60 * 1000);
          break; // Cascade to next model immediately
        } else if (status === 503 || status === 500) {
          // Transient server pressure: short 30-second cooldown
          modelDegradedUntil.set(model, Date.now() + 30 * 1000);
          if (attempt === 0) continue; // Try once more before cascading
        }
      }
    }
  }
  throw lastError;
}
Enter fullscreen mode Exit fullscreen mode

If the primary model experiences transient capacity pressure, the call cascades to the next tier in milliseconds, returning a valid response without breaking the user experience.


πŸš€ Deploying to Google Cloud Run

Deploying a full-stack container to Google Cloud Run provides several key operational advantages:

  • Unified Container: Both the static client assets and the Node.js Express server run in one container on port 3000.
  • Scale to Zero: During idle hours, Cloud Run instances scale down to zero, minimizing resource consumption.
  • Native Secret Manager Binding: Cloud Run mounts secrets directly into environment variables without requiring .env files in production images.

Step-by-Step Deployment Walkthrough

1. Enable Required Google Cloud APIs

gcloud services enable \
  run.googleapis.com \
  secretmanager.googleapis.com \
  firestore.googleapis.com
Enter fullscreen mode Exit fullscreen mode

2. Configure Secret Manager for the Gemini API Key

# Create the secret
gcloud secrets create GEMINI_API_KEY --replication-policy="automatic"

# Set the secret value
echo -n "YOUR_GEMINI_API_KEY" | gcloud secrets versions add GEMINI_API_KEY --data-file=-

# Grant Cloud Run's service account permission to access the secret
PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)")

gcloud secrets add-iam-policy-binding GEMINI_API_KEY \
  --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
Enter fullscreen mode Exit fullscreen mode

3. Build & Deploy to Cloud Run

# Deploy container directly from the application source
gcloud run deploy gemini-reflection-journal \
  --source . \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-secrets GEMINI_API_KEY=GEMINI_API_KEY:latest

# Attach the required Cloud Run AI Challenge label for automated verification
gcloud run services update gemini-reflection-journal \
  --update-labels=dev-tutorial=cloud-run-ai-challenge \
  --region=us-central1
Enter fullscreen mode Exit fullscreen mode

Once deployed, Cloud Run provides a production HTTPS URL with automatic TLS termination and scalable request routing.


πŸ“ˆ Key Takeaways

  1. Sub-second latency enables natural conversational flow: Using Gemini 3.6 Flash ensures that reflection feedback returns within seconds of finishing dictation, preventing awkward conversational pauses.
  2. Direct DOM synchronization solves hybrid 3D UI lag: Decoupling 2D HTML labels from React's virtual DOM and projecting them directly in the WebGL requestAnimationFrame loop delivers smooth 60 FPS performance without trailing or jitter.
  3. Cross-session callbacks turn logs into insights: Connecting historical reflections with current entries via Socratic callbacks bridges cognitive patterns that users would otherwise miss.
  4. Containerized serverless simplifies full-stack delivery: Running a bundled React + Express service in Cloud Run eliminates the need to coordinate separate hosting environments, API gateways, or complex reverse proxies.

Submitted as part of the Google Cloud Run AI Challenge.

#AccelerateAIwithCloudRun #GoogleCloud #CloudRun #Gemini #GeminiAI #GoogleGenAI #Firestore #Firebase

Top comments (0)