<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aman singh </title>
    <description>The latest articles on DEV Community by Aman singh  (@officialasforge).</description>
    <link>https://dev.to/officialasforge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4113655%2Ffc6a4b19-4561-4cfe-9c5b-e0fbfc5bf71a.png</url>
      <title>DEV Community: Aman singh </title>
      <link>https://dev.to/officialasforge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/officialasforge"/>
    <language>en</language>
    <item>
      <title>How I Built a Cinematic 3D Fitness Website with React Three Fiber and Three.js</title>
      <dc:creator>Aman singh </dc:creator>
      <pubDate>Sat, 26 Sep 2026 15:18:13 +0000</pubDate>
      <link>https://dev.to/officialasforge/how-i-built-a-cinematic-3d-fitness-website-with-react-three-fiber-and-threejs-4dk2</link>
      <guid>https://dev.to/officialasforge/how-i-built-a-cinematic-3d-fitness-website-with-react-three-fiber-and-threejs-4dk2</guid>
      <description>&lt;h1&gt;
  
  
  Building a Cinematic 3D Fitness Website with React Three Fiber and Three.js
&lt;/h1&gt;

&lt;p&gt;Modern fitness websites don't have to look like a simple collection of cards, buttons, and text.&lt;/p&gt;

&lt;p&gt;While building my fitness platform &lt;strong&gt;ASFORGE FITNESS&lt;/strong&gt;, I wanted the website to feel more like an interactive experience. The main goal was to combine a fitness platform with a cinematic 3D interface, animated characters, glowing effects, and an AI fitness coach.&lt;/p&gt;

&lt;p&gt;In this article, I'll share how I built the 3D experience using &lt;strong&gt;React, Vite, React Three Fiber, Three.js, and Drei&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What I Wanted to Build
&lt;/h2&gt;

&lt;p&gt;The idea was to create a fitness website with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A cinematic hero section&lt;/li&gt;
&lt;li&gt;An interactive 3D character&lt;/li&gt;
&lt;li&gt;Character animations&lt;/li&gt;
&lt;li&gt;Animated background effects&lt;/li&gt;
&lt;li&gt;Stars and particles&lt;/li&gt;
&lt;li&gt;Energy rings and glow effects&lt;/li&gt;
&lt;li&gt;AI fitness coach integration&lt;/li&gt;
&lt;li&gt;Workout and diet sections&lt;/li&gt;
&lt;li&gt;Membership plans&lt;/li&gt;
&lt;li&gt;A modern responsive interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of making the 3D scene a completely separate application, I wanted it to work naturally inside a normal React website.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tech Stack
&lt;/h2&gt;

&lt;p&gt;The main technologies I used were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Three.js&lt;/li&gt;
&lt;li&gt;React Three Fiber&lt;/li&gt;
&lt;li&gt;Drei&lt;/li&gt;
&lt;li&gt;React Three Postprocessing&lt;/li&gt;
&lt;li&gt;GLB 3D models&lt;/li&gt;
&lt;li&gt;Gemini AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The normal website UI is built with React, while React Three Fiber is responsible for rendering the 3D experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚛️ Why React Three Fiber?
&lt;/h2&gt;

&lt;p&gt;Three.js is extremely powerful for creating 3D experiences on the web.&lt;/p&gt;

&lt;p&gt;However, when working inside a React application, managing Three.js scenes directly can become difficult.&lt;/p&gt;

&lt;p&gt;React Three Fiber provides a React-based way to work with Three.js.&lt;/p&gt;

&lt;p&gt;Instead of manually creating and managing everything with traditional Three.js code, I could structure the 3D scene using React components.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
jsx
&amp;lt;Canvas&amp;gt;
  &amp;lt;ambientLight /&amp;gt;
  &amp;lt;directionalLight position={[2, 4, 5]} /&amp;gt;

  &amp;lt;HeroCharacter /&amp;gt;

  &amp;lt;Stars /&amp;gt;
&amp;lt;/Canvas&amp;gt;

This made it much easier to keep the 3D scene organized as part of the React application.

🧍 Adding a 3D Character

One of the most important parts of the hero section was the 3D character.

I used a .glb model and loaded it with Drei's useGLTF.

A simplified version looks like this:

import { useGLTF } from "@react-three/drei";

function HeroCharacter() {
  const { scene } = useGLTF("/models/character.glb");

  return (
    &amp;lt;primitive
      object={scene}
      scale={1}
      position={[0, -1, 0]}
    /&amp;gt;
  );
}

The model is then placed inside the React Three Fiber canvas.

The important part was not simply loading the model.

The real challenge was getting the scale, camera position, and character position right.

📷 Camera Position Matters

A 3D model can look completely different depending on the camera.

Initially, my character was either too large, too small, or partially outside the screen.

I experimented with camera settings such as:

&amp;lt;Canvas
  camera={{
    position: [0, 0.5, 6],
    fov: 40,
  }}
&amp;gt;

I then adjusted the character's scale and position until the model stayed inside the hero section while still looking large and cinematic.

This was one of the most important parts of creating the final composition.

🎬 Playing the Character Animation

The 3D model also needed to feel alive.

For animated GLB models, I used Drei's animation support with useAnimations.

A simplified example:

import { useEffect } from "react";
import { useAnimations, useGLTF } from "@react-three/drei";

function AnimatedCharacter() {
  const { scene, animations } = useGLTF(
    "/models/character.glb"
  );

  const { actions } = useAnimations(
    animations,
    scene
  );

  useEffect(() =&amp;gt; {
    const animation = Object.values(actions)[0];

    if (animation) {
      animation.reset().fadeIn(0.5).play();
    }

    return () =&amp;gt; {
      if (animation) {
        animation.fadeOut(0.5);
      }
    };
  }, [actions]);

  return &amp;lt;primitive object={scene} /&amp;gt;;
}

This allowed the model's built-in animation to play inside the website.

✨ Creating the Cinematic Environment

A plain 3D model wasn't enough.

I wanted the scene to feel energetic and connected to the fitness theme.

So I combined several visual effects:

Stars
Particles
Glowing rings
Background effects
Energy effects
Lighting
Bloom
Animated elements

The result was much more dynamic than simply placing a model on a static background.

🌌 Stars and Particles

For the background, I used Drei's Stars component.

For example:

&amp;lt;Stars
  radius={80}
  depth={50}
  count={3000}
  factor={4}
  saturation={0}
  fade
  speed={1}
/&amp;gt;

This gives the background a subtle space-like environment.

I also experimented with additional particle effects to make the scene feel more alive.

💡 Lighting

Lighting makes a huge difference in 3D.

I used multiple light sources instead of relying on only one light.

For example:

&amp;lt;ambientLight intensity={0.5} /&amp;gt;

&amp;lt;directionalLight
  position={[3, 5, 2]}
  intensity={2}
/&amp;gt;

The goal was to make the character clearly visible while maintaining a dramatic look.

🌟 Bloom and Glow

To make bright elements feel more cinematic, I used post-processing.

The setup included:

&amp;lt;EffectComposer&amp;gt;
  &amp;lt;Bloom
    intensity={1.2}
    luminanceThreshold={0.2}
    luminanceSmoothing={0.9}
  /&amp;gt;
&amp;lt;/EffectComposer&amp;gt;

Bloom helped create the glowing appearance around bright objects and energy effects.

This was especially useful for the rings, particles, and other bright elements in the scene.

🔄 Keeping the Scene Animated

A static 3D scene can quickly feel boring.

React Three Fiber provides useFrame, which can be used to update objects on every rendered frame.

For example:

useFrame((state, delta) =&amp;gt; {
  mesh.rotation.y += delta * 0.3;
});

This can be used for:

Rotation
Floating effects
Animated rings
Particle movement
Camera effects
Other continuous animations

I used this approach to make different parts of the hero scene feel dynamic.

🧩 Integrating 3D with a Normal React UI

One of the interesting parts of this project was combining a traditional website UI with the 3D scene.

The page still contains normal React elements such as:

Navigation
Buttons
Text
Fitness plans
Workout sections
AI Coach section
Contact forms

The 3D scene is placed inside the hero section rather than replacing the entire website.

Conceptually, the structure looks like:

React Application
│
├── Navigation
│
├── Hero Section
│   ├── Text Content
│   ├── CTA Buttons
│   └── React Three Fiber Scene
│       ├── 3D Character
│       ├── Lights
│       ├── Stars
│       ├── Particles
│       ├── Energy Effects
│       └── Post Processing
│
├── AI Coach
├── Workout Sections
├── Membership
└── Contact

This structure made it possible to use 3D where it actually adds value instead of turning the whole website into a 3D application.

🤖 Connecting the 3D Experience with an AI Fitness Coach

The 3D hero was only one part of the project.

The website also includes an AI fitness coach powered by Gemini.

The AI Coach provides trainer-style interaction while the 3D scene creates the visual identity of the platform.

This combination gives the website two different experiences:

Visual experience:

3D character + animations + cinematic effects

Interactive experience:

AI fitness coach + conversations + fitness guidance

🌍 Multilingual AI Coach

Another part of the project is multilingual support.

The AI Coach can work with a large number of languages, allowing users to interact with the fitness platform beyond a single language.

This became especially useful for the idea of making the platform accessible to users from different backgrounds.

The language handling is kept separate from the 3D rendering system, so the two systems can evolve independently.

🐛 Some Challenges I Faced

Building the 3D section wasn't completely straightforward.

1. Model going outside the screen

The first challenge was positioning the character correctly.

Changing the scale also changed how large the character appeared relative to the camera.

I had to balance:

Camera position
+
Field of view
+
Model scale
+
Model position

until the character looked good on the screen.

2. Animation setup

A GLB model may contain one or multiple animations.

So I had to load the animation clips and start the appropriate action instead of assuming the model would automatically animate.

3. Too many visual effects

Adding more effects does not always make a scene better.

Too many particles, lights, or post-processing effects can make the scene visually noisy and can also increase rendering work.

I therefore treated the effects as supporting elements rather than the main focus.

4. React and Three.js integration

Another challenge was keeping the 3D components modular.

Instead of putting everything into one large component, I separated different responsibilities into components such as:

HeroScene
Character
Energy Effects
Particles
Rings
Lighting

This made the project easier to modify.

📱 Making the Experience Fit the Website

The 3D scene also had to work with the rest of the website.

A cinematic desktop layout can easily break on smaller screens.

So the scene needs to be considered together with:

Hero text
Buttons
Navigation
Screen size
Character scale
Camera position

The goal is not just to make the 3D scene look good on one screen, but to make the overall hero section usable.

🚀 What I Learned

This project taught me that adding 3D to a website is not just about creating a cool model.

The important parts are:

Composition
Camera positioning
Lighting
Animation
Performance
UI integration
Responsive design

React Three Fiber made the process much easier because I could build the 3D experience using a React component structure.

🔮 What's Next?

The ASFORGE FITNESS project is still evolving.

Some of the areas I want to continue improving include:

Better 3D interactions
More fitness-focused animations
Improved mobile performance
More AI features
Better workout personalization
More interactive exercise demonstrations

The long-term goal is to bring the same experience to a mobile application as well.

🏁 Final Thoughts

Building a cinematic 3D website showed me how much difference visual interaction can make when it is combined with a useful product.

Instead of using 3D only as decoration, I wanted it to become part of the identity of the fitness platform.

The combination of React + React Three Fiber + Three.js + AI gave me a strong foundation for experimenting with interactive web experiences.

I'm continuing to improve ASFORGE FITNESS and explore how AI and 3D can work together to create better fitness experiences.

🔗 Technologies Used

Frontend: React, Vite, JavaScript

3D: Three.js, React Three Fiber, Drei

3D Assets: GLB models and animations

Effects: React Three Postprocessing, Bloom, particles and lighting

AI: Gemini

If you're also experimenting with React Three Fiber, I'd love to hear what kind of 3D experiences you're building.

#react #threejs #javascript #webdev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>threejs</category>
    </item>
    <item>
      <title>How I Built a Multilingual AI Fitness Coach with Gemini and 90+ Languages</title>
      <dc:creator>Aman singh </dc:creator>
      <pubDate>Wed, 23 Sep 2026 15:33:00 +0000</pubDate>
      <link>https://dev.to/officialasforge/how-i-built-a-multilingual-ai-fitness-coach-with-gemini-and-90-languages-1pep</link>
      <guid>https://dev.to/officialasforge/how-i-built-a-multilingual-ai-fitness-coach-with-gemini-and-90-languages-1pep</guid>
      <description>&lt;p&gt;How I Built a Multilingual AI Fitness Coach with Gemini and 90+ Languages&lt;/p&gt;

&lt;p&gt;Building an AI fitness coach is one thing.&lt;/p&gt;

&lt;p&gt;Building one that can communicate with users across 90+ languages, understand fitness-related questions, respond naturally, and also support voice interaction was a much more interesting challenge.&lt;/p&gt;

&lt;p&gt;For my fitness project ASFORGE FITNESS, I wanted to go beyond a traditional chatbot.&lt;/p&gt;

&lt;p&gt;So I built Titan AI Coach — a multilingual AI fitness assistant powered by Google Gemini, React, JavaScript, and a Node.js backend.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain how I approached the multilingual AI system and how the different parts work together.&lt;/p&gt;

&lt;p&gt;🤖 What is Titan AI Coach?&lt;/p&gt;

&lt;p&gt;Titan AI Coach is the AI assistant inside my fitness platform.&lt;/p&gt;

&lt;p&gt;The goal was to make it feel more like a digital fitness coach rather than a simple text chatbot.&lt;/p&gt;

&lt;p&gt;It can help users with things like:&lt;/p&gt;

&lt;p&gt;🏋️ Workout guidance&lt;br&gt;
🥗 Nutrition-related questions&lt;br&gt;
💪 Exercise guidance&lt;br&gt;
📋 Fitness routines&lt;br&gt;
💬 General fitness conversations&lt;br&gt;
🌍 Multilingual conversations&lt;br&gt;
🎙️ Voice interaction&lt;/p&gt;

&lt;p&gt;The project combines an interactive frontend with a backend AI service.&lt;/p&gt;

&lt;p&gt;🌍 Why 90+ Languages?&lt;/p&gt;

&lt;p&gt;One of the biggest ideas behind this project was accessibility.&lt;/p&gt;

&lt;p&gt;A fitness assistant shouldn't be limited to English users.&lt;/p&gt;

&lt;p&gt;Different users are comfortable communicating in different languages, so I wanted Titan to support a large number of languages.&lt;/p&gt;

&lt;p&gt;The application maintains a language list and allows the user to select their preferred language.&lt;/p&gt;

&lt;p&gt;The selected language becomes part of the AI interaction so that the response can be generated in the user's chosen language.&lt;/p&gt;

&lt;p&gt;For example, a user can interact using languages such as:&lt;/p&gt;

&lt;p&gt;English&lt;br&gt;
Hindi&lt;br&gt;
Spanish&lt;br&gt;
French&lt;br&gt;
German&lt;br&gt;
Japanese&lt;br&gt;
Portuguese&lt;br&gt;
Italian&lt;/p&gt;

&lt;p&gt;…and many more.&lt;/p&gt;

&lt;p&gt;The exact supported language list is maintained by the application rather than hard-coding a small number of languages.&lt;/p&gt;

&lt;p&gt;🧠 Using Gemini as the AI Brain&lt;/p&gt;

&lt;p&gt;For the AI responses, I used Google Gemini.&lt;/p&gt;

&lt;p&gt;The basic architecture looks like this:&lt;/p&gt;

&lt;p&gt;User&lt;br&gt;
  ↓&lt;br&gt;
React Frontend&lt;br&gt;
  ↓&lt;br&gt;
Titan AI Coach&lt;br&gt;
  ↓&lt;br&gt;
Node.js / Express Backend&lt;br&gt;
  ↓&lt;br&gt;
Google Gemini API&lt;br&gt;
  ↓&lt;br&gt;
AI Response&lt;br&gt;
  ↓&lt;br&gt;
React UI&lt;/p&gt;

&lt;p&gt;The important part is that the Gemini API key is not exposed directly in the frontend.&lt;/p&gt;

&lt;p&gt;Instead, the frontend communicates with my backend, and the backend communicates with Gemini.&lt;/p&gt;

&lt;p&gt;This keeps the API credential on the server side.&lt;/p&gt;

&lt;p&gt;⚛️ React Frontend&lt;/p&gt;

&lt;p&gt;The frontend is built with React.&lt;/p&gt;

&lt;p&gt;The AI Coach UI handles:&lt;/p&gt;

&lt;p&gt;User messages&lt;br&gt;
AI responses&lt;br&gt;
Language selection&lt;br&gt;
Chat history&lt;br&gt;
Loading states&lt;br&gt;
Voice interaction&lt;br&gt;
Error handling&lt;/p&gt;

&lt;p&gt;The frontend sends the user's request to the backend instead of directly calling Gemini.&lt;/p&gt;

&lt;p&gt;A simplified version of the flow looks like:&lt;/p&gt;

&lt;p&gt;const response = await fetch("/api/chat", {&lt;br&gt;
  method: "POST",&lt;br&gt;
  headers: {&lt;br&gt;
    "Content-Type": "application/json"&lt;br&gt;
  },&lt;br&gt;
  body: JSON.stringify({&lt;br&gt;
    message,&lt;br&gt;
    language&lt;br&gt;
  })&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;const data = await response.json();&lt;/p&gt;

&lt;p&gt;The backend then processes the request and sends the AI response back to React.&lt;/p&gt;

&lt;p&gt;🟢 Node.js + Express Backend&lt;/p&gt;

&lt;p&gt;I used Node.js with Express as the backend layer.&lt;/p&gt;

&lt;p&gt;The backend is responsible for:&lt;/p&gt;

&lt;p&gt;Receiving the user's message&lt;br&gt;
Receiving the selected language&lt;br&gt;
Preparing the Gemini request&lt;br&gt;
Calling the Gemini API&lt;br&gt;
Handling errors&lt;br&gt;
Returning the response to the frontend&lt;/p&gt;

&lt;p&gt;This separation also makes the application easier to maintain.&lt;/p&gt;

&lt;p&gt;Instead of putting everything inside the React application, the AI logic stays behind the backend API.&lt;/p&gt;

&lt;p&gt;🌐 Language Switching&lt;/p&gt;

&lt;p&gt;The user can select a language from the AI Coach interface.&lt;/p&gt;

&lt;p&gt;The selected language is then passed to the backend.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;/p&gt;

&lt;p&gt;Selected Language&lt;br&gt;
       ↓&lt;br&gt;
User Message&lt;br&gt;
       ↓&lt;br&gt;
Backend&lt;br&gt;
       ↓&lt;br&gt;
Gemini&lt;br&gt;
       ↓&lt;br&gt;
Response in Selected Language&lt;/p&gt;

&lt;p&gt;This means the same AI Coach can be used by users with different language preferences without creating a separate chatbot for every language.&lt;/p&gt;

&lt;p&gt;🎙️ Adding Voice Interaction&lt;/p&gt;

&lt;p&gt;I also wanted Titan to support voice interaction.&lt;/p&gt;

&lt;p&gt;For this, the browser's speech capabilities are used for voice input and speech output.&lt;/p&gt;

&lt;p&gt;The basic experience is:&lt;/p&gt;

&lt;p&gt;User speaks&lt;br&gt;
    ↓&lt;br&gt;
Speech Recognition&lt;br&gt;
    ↓&lt;br&gt;
Text&lt;br&gt;
    ↓&lt;br&gt;
Gemini AI&lt;br&gt;
    ↓&lt;br&gt;
AI Response&lt;br&gt;
    ↓&lt;br&gt;
Speech Synthesis&lt;br&gt;
    ↓&lt;br&gt;
User hears response&lt;/p&gt;

&lt;p&gt;This makes the interaction more natural than typing every question.&lt;/p&gt;

&lt;p&gt;It also makes the AI Coach feel closer to an actual digital assistant.&lt;/p&gt;

&lt;p&gt;🔄 Retry and Fallback Handling&lt;/p&gt;

&lt;p&gt;AI APIs can sometimes fail because of temporary network problems, rate limits, or service issues.&lt;/p&gt;

&lt;p&gt;Instead of immediately showing an error to the user, I added retry and fallback handling to make the experience more reliable.&lt;/p&gt;

&lt;p&gt;The general flow is:&lt;/p&gt;

&lt;p&gt;Request&lt;br&gt;
  ↓&lt;br&gt;
Gemini API&lt;br&gt;
  ↓&lt;br&gt;
Success? ── Yes → Return Response&lt;br&gt;
  │&lt;br&gt;
  No&lt;br&gt;
  ↓&lt;br&gt;
Retry&lt;br&gt;
  ↓&lt;br&gt;
Success? ── Yes → Return Response&lt;br&gt;
  │&lt;br&gt;
  No&lt;br&gt;
  ↓&lt;br&gt;
Fallback / Error Message&lt;/p&gt;

&lt;p&gt;This is especially useful for a user-facing application where a temporary API issue shouldn't completely break the interface.&lt;/p&gt;

&lt;p&gt;🔐 Keeping API Keys Secure&lt;/p&gt;

&lt;p&gt;One of the most important lessons while building this project was never exposing private API keys in frontend code.&lt;/p&gt;

&lt;p&gt;For example, sensitive keys should not be hard-coded like this:&lt;/p&gt;

&lt;p&gt;const API_KEY = "YOUR_SECRET_KEY";&lt;/p&gt;

&lt;p&gt;Instead, sensitive credentials are stored in environment variables on the backend.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;GEMINI_API_KEY=your_secret_key&lt;/p&gt;

&lt;p&gt;The frontend talks to the backend, and the backend uses the secret key.&lt;/p&gt;

&lt;p&gt;This is much safer than exposing the key to every browser user.&lt;/p&gt;

&lt;p&gt;🏋️ Making the AI Fitness-Specific&lt;/p&gt;

&lt;p&gt;Titan isn't designed as a completely generic chatbot.&lt;/p&gt;

&lt;p&gt;The project is a fitness platform, so the AI experience is designed around fitness-related conversations.&lt;/p&gt;

&lt;p&gt;The surrounding website provides context through features such as:&lt;/p&gt;

&lt;p&gt;Workout routines&lt;br&gt;
Diet routines&lt;br&gt;
Exercise demonstrations&lt;br&gt;
Fitness tracking&lt;br&gt;
Premium fitness plans&lt;br&gt;
AI coaching&lt;/p&gt;

&lt;p&gt;The idea is to make the AI part of the complete fitness experience instead of having an unrelated chatbot sitting on a fitness website.&lt;/p&gt;

&lt;p&gt;🧩 Technologies Used&lt;/p&gt;

&lt;p&gt;Here are the main technologies behind this project:&lt;/p&gt;

&lt;p&gt;Technology  Purpose&lt;br&gt;
React   Frontend UI&lt;br&gt;
JavaScript  Application logic&lt;br&gt;
Vite    Frontend development/build&lt;br&gt;
Node.js Backend runtime&lt;br&gt;
Express API server&lt;br&gt;
Google Gemini   AI responses&lt;br&gt;
Browser Speech APIs Voice interaction&lt;br&gt;
Three.js    3D/interactive experience&lt;br&gt;
Supabase    Application data/backend services&lt;br&gt;
🚧 Challenges I Faced&lt;/p&gt;

&lt;p&gt;Building this system wasn't just about connecting an AI API.&lt;/p&gt;

&lt;p&gt;Some of the challenges included:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Multilingual responses&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Supporting many languages required a clean way to manage language selection and pass that information through the AI request.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;API failures&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Temporary failures required retry and fallback handling.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Voice interaction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Speech recognition and speech synthesis behave differently across browsers, so voice features need careful handling.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping API credentials away from frontend code was another important part of the architecture.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User experience&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI shouldn't feel like a plain API response.&lt;/p&gt;

&lt;p&gt;The goal was to make Titan feel integrated with the rest of the fitness platform.&lt;/p&gt;

&lt;p&gt;🔮 What's Next?&lt;/p&gt;

&lt;p&gt;The project is still evolving.&lt;/p&gt;

&lt;p&gt;Some of the areas I'm working toward include:&lt;/p&gt;

&lt;p&gt;Better AI fitness conversations&lt;br&gt;
More personalized coaching&lt;br&gt;
Improved voice interaction&lt;br&gt;
Better workout recommendations&lt;br&gt;
User progress integration&lt;br&gt;
More intelligent fitness guidance&lt;br&gt;
Further improvements to the multilingual experience&lt;br&gt;
Mobile application expansion&lt;/p&gt;

&lt;p&gt;The long-term goal is to turn the project into a more complete AI-powered fitness platform.&lt;/p&gt;

&lt;p&gt;💡 What I Learned&lt;/p&gt;

&lt;p&gt;This project taught me that building an AI application is not only about calling an AI API.&lt;/p&gt;

&lt;p&gt;A good AI product also needs:&lt;/p&gt;

&lt;p&gt;A clean frontend&lt;br&gt;
Secure backend architecture&lt;br&gt;
Error handling&lt;br&gt;
Good user experience&lt;br&gt;
Voice interaction&lt;br&gt;
Language support&lt;br&gt;
Reliable API communication&lt;br&gt;
Proper separation between frontend and backend&lt;/p&gt;

&lt;p&gt;The AI is only one part of the complete system.&lt;/p&gt;

&lt;p&gt;🚀 Final Thoughts&lt;/p&gt;

&lt;p&gt;Titan AI Coach started as an idea to add an AI assistant to my fitness website.&lt;/p&gt;

&lt;p&gt;It gradually became a much bigger experiment involving AI, multilingual communication, voice interaction, React, Node.js, Three.js and backend architecture.&lt;/p&gt;

&lt;p&gt;The most interesting part for me was seeing a fitness assistant communicate across many different languages while remaining part of the same application.&lt;/p&gt;

&lt;p&gt;I'm continuing to improve ASFORGE FITNESS and experiment with new ways to combine AI with fitness technology.&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

&lt;p&gt;If you're also building AI-powered applications, I'd love to hear what you're working on.&lt;/p&gt;

&lt;p&gt;🔗 Project&lt;/p&gt;

&lt;p&gt;ASFORGE FITNESS — AI-Powered Fitness Platform&lt;/p&gt;

&lt;p&gt;Built with React • JavaScript • Gemini AI • Node.js • Three.js&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>How I Added Razorpay Payments to My React Fitness Website</title>
      <dc:creator>Aman singh </dc:creator>
      <pubDate>Tue, 15 Sep 2026 15:32:14 +0000</pubDate>
      <link>https://dev.to/officialasforge/how-i-added-razorpay-payments-to-my-react-fitness-website-3dgj</link>
      <guid>https://dev.to/officialasforge/how-i-added-razorpay-payments-to-my-react-fitness-website-3dgj</guid>
      <description>&lt;p&gt;Building a fitness website is not only about workouts, animations, and a good-looking UI.&lt;/p&gt;

&lt;p&gt;If you want to turn a fitness platform into a real product, you also need a way for users to purchase memberships.&lt;/p&gt;

&lt;p&gt;While building my React-based fitness website, I decided to integrate &lt;strong&gt;Razorpay&lt;/strong&gt; so users could join different membership plans directly from the website.&lt;/p&gt;

&lt;p&gt;In this article, I'll show how I implemented the payment flow, connected React with a Node.js backend, created Razorpay orders securely, and verified payments on the server.&lt;/p&gt;

&lt;p&gt;The project uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React + Vite&lt;/li&gt;
&lt;li&gt;Node.js + Express&lt;/li&gt;
&lt;li&gt;Razorpay&lt;/li&gt;
&lt;li&gt;Supabase&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to create a simple flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Plan → Create Order → Open Razorpay Checkout → Complete Payment → Verify Payment&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setting Up the Membership Plans
&lt;/h2&gt;

&lt;p&gt;For the fitness website, I created three simple membership plans:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;₹99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Premium&lt;/td&gt;
&lt;td&gt;₹149&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elite&lt;/td&gt;
&lt;td&gt;₹199&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each plan has its own &lt;strong&gt;Join Now&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;When a user clicks the button, the frontend sends the selected plan to the backend instead of directly trusting the price from the browser.&lt;/p&gt;

&lt;p&gt;This is important because payment amounts should be controlled on the server side.&lt;/p&gt;

&lt;p&gt;For example, my backend keeps the plan prices like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MEMBERSHIP_PLANS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Basic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9900&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;premium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Premium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14900&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;elite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;19900&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;Razorpay&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Backend&lt;/span&gt;

&lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;did&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Razorpay&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="nx"&gt;directly&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="nx"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Instead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;frontend&lt;/span&gt; &lt;span class="nx"&gt;sends&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;Express&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="nx"&gt;then&lt;/span&gt; &lt;span class="nx"&gt;creates&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Razorpay&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;side&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="nx"&gt;configured&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;basic&lt;/span&gt; &lt;span class="nx"&gt;flow&lt;/span&gt; &lt;span class="nx"&gt;looks&lt;/span&gt; &lt;span class="nx"&gt;like&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
React Frontend&lt;br&gt;
      ↓&lt;br&gt;
Select Membership&lt;br&gt;
      ↓&lt;br&gt;
POST /api/create-order&lt;br&gt;
      ↓&lt;br&gt;
Node.js + Express&lt;br&gt;
      ↓&lt;br&gt;
Razorpay API&lt;br&gt;
      ↓&lt;br&gt;
Order ID&lt;br&gt;
      ↓&lt;br&gt;
Razorpay Checkout&lt;/p&gt;

&lt;p&gt;app.post("/api/create-order", async (req, res) =&amp;gt; {&lt;br&gt;
  try {&lt;br&gt;
    const { planId } = req.body;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const plan = MEMBERSHIP_PLANS[planId];

if (!plan) {
  return res.status(400).json({
    error: "Invalid membership plan"
  });
}

const razorpay = getRazorpayClient();

const order = await razorpay.orders.create({
  amount: plan.amount,
  currency: "INR",
  receipt: `receipt_${Date.now()}`
});

res.json({
  orderId: order.id,
  amount: order.amount,
  currency: order.currency,
  planName: plan.name
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error(error);&lt;br&gt;
    res.status(500).json({&lt;br&gt;
      error: "Unable to create order"&lt;br&gt;
    });&lt;br&gt;
  }&lt;br&gt;
});&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Opening Razorpay Checkout from React
&lt;/h2&gt;

&lt;p&gt;Once the backend creates the order, it returns the Razorpay &lt;code&gt;orderId&lt;/code&gt; to the React frontend.&lt;/p&gt;

&lt;p&gt;The frontend then uses the Razorpay Checkout SDK to open the payment window.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User clicks "Join Now"
        ↓
React calls /api/create-order
        ↓
Backend creates Razorpay Order
        ↓
Frontend receives Order ID
        ↓
Razorpay Checkout opens
        ↓
User selects payment method
        ↓
Payment is completed

const handlePayment = async (planId) =&amp;gt; {
  const response = await fetch("/api/create-order", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ planId })
  });

  const order = await response.json();

  const options = {
    key: import.meta.env.VITE_RAZORPAY_KEY_ID,
    amount: order.amount,
    currency: "INR",
    name: "Official ASForge",
    description: `${order.planName} Membership`,
    order_id: order.orderId,

    handler: async (paymentResponse) =&amp;gt; {
      // Payment verification happens here
    }
  };

  const razorpay = new window.Razorpay(options);
  razorpay.open();
};

## 4. Verifying the Payment Securely

Opening the Razorpay Checkout is only one part of the payment process.

After a successful payment, Razorpay returns payment details such as:

- `razorpay_payment_id`
- `razorpay_order_id`
- `razorpay_signature`

I send these values back to my backend for verification.

The backend generates an HMAC SHA256 signature using the Razorpay Key Secret and compares it with the signature received from Razorpay.

A simplified implementation looks like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
js&lt;br&gt;
app.post("/api/verify-payment", (req, res) =&amp;gt; {&lt;br&gt;
  try {&lt;br&gt;
    const {&lt;br&gt;
      razorpay_order_id,&lt;br&gt;
      razorpay_payment_id,&lt;br&gt;
      razorpay_signature&lt;br&gt;
    } = req.body;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const generatedSignature = crypto
  .createHmac("sha256", process.env.RAZORPAY_KEY_SECRET)
  .update(
    `${razorpay_order_id}|${razorpay_payment_id}`
  )
  .digest("hex");

const isValid =
  generatedSignature === razorpay_signature;

if (!isValid) {
  return res.status(400).json({
    success: false,
    error: "Invalid payment signature"
  });
}

res.json({
  success: true,
  message: "Payment verified successfully"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error(error);&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;res.status(500).json({
  success: false,
  error: "Payment verification failed"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
});&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8 — Keeping Secrets Safe
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 5. Keeping Razorpay Secrets Safe&lt;/span&gt;

One of the most important parts of the integration was keeping the Razorpay credentials secure.

I used environment variables for the backend credentials:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
env&lt;br&gt;
RAZORPAY_KEY_ID=your_key_id&lt;br&gt;
RAZORPAY_KEY_SECRET=your_secret&lt;br&gt;
VITE_RAZORPAY_KEY_ID=your_key_id&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9 — Complete Payment Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 6. The Complete Payment Flow&lt;/span&gt;

After putting everything together, the complete membership flow looks like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
                    User&lt;br&gt;
                     ↓&lt;br&gt;
              Selects a Plan&lt;br&gt;
                     ↓&lt;br&gt;
              Clicks "Join Now"&lt;br&gt;
                     ↓&lt;br&gt;
              React Frontend&lt;br&gt;
                     ↓&lt;br&gt;
           POST /api/create-order&lt;br&gt;
                     ↓&lt;br&gt;
             Express Backend&lt;br&gt;
                     ↓&lt;br&gt;
             Razorpay API&lt;br&gt;
                     ↓&lt;br&gt;
                Order ID&lt;br&gt;
                     ↓&lt;br&gt;
           Razorpay Checkout&lt;br&gt;
                     ↓&lt;br&gt;
             User Pays&lt;br&gt;
                     ↓&lt;br&gt;
             Payment Details&lt;br&gt;
                     ↓&lt;br&gt;
          POST /api/verify-payment&lt;br&gt;
                     ↓&lt;br&gt;
          Server-Side Verification&lt;br&gt;
                     ↓&lt;br&gt;
              Payment Success&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10 — Supabase
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 7. Connecting Payments with Supabase&lt;/span&gt;

After getting the Razorpay payment flow working, I also connected Supabase to the project.

Supabase can be used to store application data such as:
&lt;span class="p"&gt;
-&lt;/span&gt; User information
&lt;span class="p"&gt;-&lt;/span&gt; Memberships
&lt;span class="p"&gt;-&lt;/span&gt; Payment records
&lt;span class="p"&gt;-&lt;/span&gt; Workout plans
&lt;span class="p"&gt;-&lt;/span&gt; Diet plans
&lt;span class="p"&gt;-&lt;/span&gt; Progress
&lt;span class="p"&gt;-&lt;/span&gt; AI usage
&lt;span class="p"&gt;-&lt;/span&gt; Website events

For example, after a verified payment, a payment record can be stored with information such as:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
user_id&lt;br&gt;
plan&lt;br&gt;
amount&lt;br&gt;
razorpay_order_id&lt;br&gt;
razorpay_payment_id&lt;br&gt;
payment_status&lt;br&gt;
created_at&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11 — Vercel Deployment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 8. Deploying the Payment System&lt;/span&gt;

My frontend is built with Vite and deployed using Vercel.

During local development, the React application communicates with my local Express server.

For production, the payment endpoints need to be available from the deployed application as well.

The production architecture can look like:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
React + Vite&lt;br&gt;
     ↓&lt;br&gt;
   Vercel&lt;br&gt;
     ↓&lt;br&gt;
 /api/create-order&lt;br&gt;
 /api/verify-payment&lt;br&gt;
     ↓&lt;br&gt;
 Razorpay&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 12 — Problems I Faced
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 9. Problems I Faced During Integration&lt;/span&gt;

While integrating Razorpay, I ran into a few common issues.

&lt;span class="gu"&gt;### Duplicate function declarations&lt;/span&gt;

While modifying the React component, I accidentally declared the same &lt;span class="sb"&gt;`handlePayment`&lt;/span&gt; function more than once.

This resulted in an error similar to:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
Identifier 'handlePayment' has already been declared&lt;/p&gt;

&lt;h2&gt;
  
  
  11. What I Learned
&lt;/h2&gt;

&lt;p&gt;This project taught me that adding payments to a website is more than simply opening a checkout window.&lt;/p&gt;

&lt;p&gt;The important concepts I learned were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Never trust payment amounts coming directly from the frontend.&lt;/li&gt;
&lt;li&gt;Create orders on the backend.&lt;/li&gt;
&lt;li&gt;Keep secret keys on the server.&lt;/li&gt;
&lt;li&gt;Verify Razorpay signatures server-side.&lt;/li&gt;
&lt;li&gt;Use environment variables for sensitive credentials.&lt;/li&gt;
&lt;li&gt;Separate development and production API configurations.&lt;/li&gt;
&lt;li&gt;Store payment records only after successful verification.&lt;/li&gt;
&lt;li&gt;Think about security while designing the payment flow, not after finishing the UI.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;In the next part, I'll explore how I built a &lt;strong&gt;multilingual AI fitness coach&lt;/strong&gt; that can interact with users in multiple languages using Gemini.&lt;/p&gt;

&lt;p&gt;Thanks for reading! 💪&lt;/p&gt;

&lt;p&gt;If you enjoyed the article, feel free to follow me for more posts about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;Three.js&lt;/li&gt;
&lt;li&gt;Full-stack development&lt;/li&gt;
&lt;li&gt;Fitness technology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build. Train. Improve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;— Aman Singh&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>payments</category>
    </item>
    <item>
      <title>How I Built an AI Fitness Coach with Gemini, React and Three.js</title>
      <dc:creator>Aman singh </dc:creator>
      <pubDate>Mon, 07 Sep 2026 10:43:39 +0000</pubDate>
      <link>https://dev.to/officialasforge/how-i-built-an-ai-fitness-coach-with-gemini-react-and-threejs-2kp</link>
      <guid>https://dev.to/officialasforge/how-i-built-an-ai-fitness-coach-with-gemini-react-and-threejs-2kp</guid>
      <description>&lt;p&gt;How I Built an AI Fitness Coach with Gemini, React and Three.js&lt;/p&gt;

&lt;p&gt;Building a website is one thing. Building a website that can actually interact with users is something completely different.&lt;/p&gt;

&lt;p&gt;While working on my fitness project under ASForge, I wanted to create more than a traditional fitness website. I wanted to build an AI fitness coach that could communicate with users, answer questions, and feel like an actual character inside the website.&lt;/p&gt;

&lt;p&gt;That's how Titan was created — an AI-powered fitness coach built with React, Three.js and Gemini.&lt;/p&gt;

&lt;p&gt;The Idea Behind Titan&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;p&gt;Create a fitness website where the AI coach is not just a chatbot sitting inside a box, but a 3D character that interacts with the user.&lt;/p&gt;

&lt;p&gt;Titan was designed to provide an interactive experience where users can:&lt;/p&gt;

&lt;p&gt;Ask fitness-related questions&lt;br&gt;
Chat with the AI&lt;br&gt;
Use voice interaction&lt;br&gt;
Receive spoken responses&lt;br&gt;
Switch between different languages&lt;br&gt;
Interact with a 3D AI character&lt;/p&gt;

&lt;p&gt;The project also gave me an opportunity to combine frontend development, 3D graphics and generative AI in one application.&lt;/p&gt;

&lt;p&gt;Technologies I Used&lt;/p&gt;

&lt;p&gt;The main technologies behind the project are:&lt;/p&gt;

&lt;p&gt;React — User interface and application logic&lt;br&gt;
Vite — Development environment and build tool&lt;br&gt;
Three.js — 3D graphics&lt;br&gt;
React Three Fiber — Using Three.js inside React&lt;br&gt;
@react-three/drei — Useful Three.js helpers&lt;br&gt;
Gemini API — AI responses&lt;br&gt;
Web Speech API — Voice recognition and text-to-speech&lt;br&gt;
JavaScript — Application logic and interactions&lt;/p&gt;

&lt;p&gt;The combination of these technologies made it possible to create both the visual and conversational parts of Titan.&lt;/p&gt;

&lt;p&gt;Creating the 3D AI Coach&lt;/p&gt;

&lt;p&gt;The first challenge was getting a 3D character into the React application.&lt;/p&gt;

&lt;p&gt;I used a GLB model for Titan and loaded it using useGLTF.&lt;/p&gt;

&lt;p&gt;A simplified version looks like this:&lt;/p&gt;

&lt;p&gt;import { useGLTF } from "@react-three/drei";&lt;/p&gt;

&lt;p&gt;function Robot() {&lt;br&gt;
  const { scene } = useGLTF("/models/ai-coach.glb");&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
    
      object={scene}&lt;br&gt;
      scale={1.35}&lt;br&gt;
      position={[0.15, -0.85, 0]}&lt;br&gt;
    /&amp;gt;&lt;br&gt;
  );&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;I then placed the model inside a React Three Fiber Canvas.&lt;/p&gt;

&lt;p&gt;&amp;lt;Canvas&lt;br&gt;
  camera={{&lt;br&gt;
    position: [0, 0.1, 3.8],&lt;br&gt;
    fov: 40,&lt;br&gt;
  }}&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;br&gt;
  
    position={[5, 5, 5]}&lt;br&gt;
    intensity={2}&lt;br&gt;
  /&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Getting the correct camera position, model scale and character placement took quite a bit of experimentation.&lt;/p&gt;

&lt;p&gt;A model can look perfect in a 3D viewer but appear too small, too large or partially outside the screen when placed inside a real webpage.&lt;/p&gt;

&lt;p&gt;Adding Animation&lt;/p&gt;

&lt;p&gt;The character should not feel like a static image.&lt;/p&gt;

&lt;p&gt;The GLB model contains animations, so I used useAnimations to play them.&lt;/p&gt;

&lt;p&gt;const { scene, animations } =&lt;br&gt;
  useGLTF("/models/ai-coach.glb");&lt;/p&gt;

&lt;p&gt;const { actions } =&lt;br&gt;
  useAnimations(animations, group);&lt;/p&gt;

&lt;p&gt;useEffect(() =&amp;gt; {&lt;br&gt;
  if (!actions) return;&lt;/p&gt;

&lt;p&gt;Object.values(actions).forEach((action) =&amp;gt; {&lt;br&gt;
    action.reset().fadeIn(0.5).play();&lt;br&gt;
  });&lt;br&gt;
}, [actions]);&lt;/p&gt;

&lt;p&gt;This made Titan feel much more alive inside the website.&lt;/p&gt;

&lt;p&gt;Connecting Titan to Gemini&lt;/p&gt;

&lt;p&gt;The next part was giving Titan a brain.&lt;/p&gt;

&lt;p&gt;Instead of hardcoding responses, I connected the application to the Gemini API.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;/p&gt;

&lt;p&gt;User&lt;br&gt;
  ↓&lt;br&gt;
Titan UI&lt;br&gt;
  ↓&lt;br&gt;
React&lt;br&gt;
  ↓&lt;br&gt;
Backend API&lt;br&gt;
  ↓&lt;br&gt;
Gemini&lt;br&gt;
  ↓&lt;br&gt;
AI Response&lt;br&gt;
  ↓&lt;br&gt;
Titan&lt;/p&gt;

&lt;p&gt;I kept the Gemini API key on the server side instead of exposing it directly in the React frontend.&lt;/p&gt;

&lt;p&gt;The frontend sends the user's message to my backend:&lt;/p&gt;

&lt;p&gt;const response = await fetch("/api/chat", {&lt;br&gt;
  method: "POST",&lt;br&gt;
  headers: {&lt;br&gt;
    "Content-Type": "application/json",&lt;br&gt;
  },&lt;br&gt;
  body: JSON.stringify({&lt;br&gt;
    message: text,&lt;br&gt;
    language: language.name,&lt;br&gt;
  }),&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;The backend then communicates with Gemini and returns the generated response.&lt;/p&gt;

&lt;p&gt;This separation is important because API keys should not be placed directly inside frontend code.&lt;/p&gt;

&lt;p&gt;Giving Titan a Voice&lt;/p&gt;

&lt;p&gt;A normal chatbot only shows text.&lt;/p&gt;

&lt;p&gt;I wanted Titan to actually speak.&lt;/p&gt;

&lt;p&gt;For that, I used browser speech capabilities.&lt;/p&gt;

&lt;p&gt;The browser's speech synthesis API can convert text into spoken audio:&lt;/p&gt;

&lt;p&gt;const utterance = new SpeechSynthesisUtterance(text);&lt;/p&gt;

&lt;p&gt;utterance.rate = 0.88;&lt;br&gt;
utterance.pitch = 0.55;&lt;br&gt;
utterance.volume = 1;&lt;/p&gt;

&lt;p&gt;speechSynthesis.speak(utterance);&lt;/p&gt;

&lt;p&gt;Now the interaction becomes:&lt;/p&gt;

&lt;p&gt;User asks a question&lt;br&gt;
        ↓&lt;br&gt;
Gemini generates the answer&lt;br&gt;
        ↓&lt;br&gt;
Titan receives the answer&lt;br&gt;
        ↓&lt;br&gt;
Speech Synthesis speaks it&lt;/p&gt;

&lt;p&gt;The exact available voices depend on the user's browser and operating system, so voice characteristics can vary between devices.&lt;/p&gt;

&lt;p&gt;Voice Input&lt;/p&gt;

&lt;p&gt;I also wanted users to be able to talk to Titan instead of typing everything.&lt;/p&gt;

&lt;p&gt;The browser's Speech Recognition API can convert speech into text.&lt;/p&gt;

&lt;p&gt;The basic concept is:&lt;/p&gt;

&lt;p&gt;const recognition =&lt;br&gt;
  new SpeechRecognition();&lt;/p&gt;

&lt;p&gt;recognition.lang = "en-IN";&lt;/p&gt;

&lt;p&gt;recognition.onresult = (event) =&amp;gt; {&lt;br&gt;
  const transcript =&lt;br&gt;
    event.results[0][0].transcript;&lt;/p&gt;

&lt;p&gt;console.log(transcript);&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;recognition.start();&lt;/p&gt;

&lt;p&gt;The recognized text can then be sent to Gemini just like a normal typed message.&lt;/p&gt;

&lt;p&gt;Handling Multiple Languages&lt;/p&gt;

&lt;p&gt;Another goal of Titan was making the experience more accessible to users who are more comfortable communicating in Indian languages.&lt;/p&gt;

&lt;p&gt;The interface includes language selection for languages such as:&lt;/p&gt;

&lt;p&gt;Hindi&lt;br&gt;
English&lt;br&gt;
Bengali&lt;br&gt;
Telugu&lt;br&gt;
Tamil&lt;br&gt;
Marathi&lt;br&gt;
Gujarati&lt;br&gt;
Kannada&lt;br&gt;
Malayalam&lt;br&gt;
Punjabi&lt;br&gt;
Odia&lt;br&gt;
Assamese&lt;br&gt;
Urdu&lt;br&gt;
Nepali&lt;br&gt;
Konkani&lt;br&gt;
Maithili&lt;br&gt;
Sanskrit&lt;br&gt;
Kashmiri&lt;br&gt;
Sindhi&lt;br&gt;
Dogri&lt;br&gt;
Manipuri&lt;br&gt;
Santali&lt;/p&gt;

&lt;p&gt;The selected language is passed along with the user's request so Titan can respond in the selected language.&lt;/p&gt;

&lt;p&gt;However, speech recognition and available voices are dependent on browser and device support, so multilingual voice behavior can vary.&lt;/p&gt;

&lt;p&gt;Making Titan Feel Like an Assistant&lt;/p&gt;

&lt;p&gt;One of the most interesting parts of the project was designing the interaction.&lt;/p&gt;

&lt;p&gt;I didn't want the AI to behave like a simple form:&lt;/p&gt;

&lt;p&gt;Question → Answer&lt;/p&gt;

&lt;p&gt;Instead, I wanted the experience to feel more like talking to an assistant.&lt;/p&gt;

&lt;p&gt;The interaction became:&lt;/p&gt;

&lt;p&gt;Speak&lt;br&gt;
  ↓&lt;br&gt;
Speech Recognition&lt;br&gt;
  ↓&lt;br&gt;
Gemini&lt;br&gt;
  ↓&lt;br&gt;
Titan Response&lt;br&gt;
  ↓&lt;br&gt;
Text-to-Speech&lt;br&gt;
  ↓&lt;br&gt;
User&lt;/p&gt;

&lt;p&gt;I also worked on interruption handling so that when the user genuinely starts another question while Titan is speaking, the current speech can be stopped and the new request can be processed.&lt;/p&gt;

&lt;p&gt;This part was more difficult than I initially expected because the microphone can sometimes pick up the computer's own speaker output.&lt;/p&gt;

&lt;p&gt;Using microphone echo cancellation and noise suppression helped, but the behavior can still depend on the hardware and environment.&lt;/p&gt;

&lt;p&gt;Designing the AI Interface&lt;/p&gt;

&lt;p&gt;I wanted the AI controls to stay separate from the 3D character.&lt;/p&gt;

&lt;p&gt;The basic structure became:&lt;/p&gt;

&lt;p&gt;┌─────────────────────────────┐&lt;br&gt;
│        Titan AI Coach       │&lt;br&gt;
│                             │&lt;br&gt;
│   Chat / Talk / Language    │&lt;br&gt;
│                             │&lt;br&gt;
│          Messages           │&lt;br&gt;
│                             │&lt;br&gt;
│              3D Titan       │&lt;br&gt;
│                  🤖         │&lt;br&gt;
└─────────────────────────────┘&lt;/p&gt;

&lt;p&gt;The 3D character is rendered in its own layer while the chat and controls are placed above or behind it using CSS positioning and z-index.&lt;/p&gt;

&lt;p&gt;This creates the impression that Titan is actually standing inside the interface rather than being another ordinary HTML element.&lt;/p&gt;

&lt;p&gt;Problems I Faced&lt;/p&gt;

&lt;p&gt;The project wasn't straightforward.&lt;/p&gt;

&lt;p&gt;Some of the biggest challenges were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;3D Model Positioning&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The character was sometimes too small or moved outside the visible area.&lt;/p&gt;

&lt;p&gt;I had to adjust:&lt;/p&gt;

&lt;p&gt;Camera position&lt;br&gt;
Field of view&lt;br&gt;
Model scale&lt;br&gt;
Model position&lt;br&gt;
Canvas dimensions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Voice Interruption&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Speech recognition could sometimes detect audio coming from Titan itself.&lt;/p&gt;

&lt;p&gt;This created unexpected interruptions.&lt;/p&gt;

&lt;p&gt;I experimented with:&lt;/p&gt;

&lt;p&gt;Echo cancellation&lt;br&gt;
Noise suppression&lt;br&gt;
Automatic gain control&lt;br&gt;
Microphone monitoring&lt;br&gt;
Speech recognition confirmation&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;API Availability&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI APIs can occasionally return temporary errors or become unavailable during periods of high demand.&lt;/p&gt;

&lt;p&gt;This made retry handling and proper error messages important.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keeping API Keys Secure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I initially focused on making the AI work, but quickly realized that API credentials should never be exposed in frontend code.&lt;/p&gt;

&lt;p&gt;Moving the API call to a backend was an important architectural improvement.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;This project taught me that building an AI application isn't only about calling an AI API.&lt;/p&gt;

&lt;p&gt;There are several layers involved:&lt;/p&gt;

&lt;p&gt;UI/UX&lt;br&gt;
  +&lt;br&gt;
3D Graphics&lt;br&gt;
  +&lt;br&gt;
AI&lt;br&gt;
  +&lt;br&gt;
Voice&lt;br&gt;
  +&lt;br&gt;
Backend&lt;br&gt;
  +&lt;br&gt;
Browser APIs&lt;/p&gt;

&lt;p&gt;Each layer has its own challenges.&lt;/p&gt;

&lt;p&gt;Three.js taught me about cameras, lighting, models and 3D positioning.&lt;/p&gt;

&lt;p&gt;React taught me how to manage application state and UI.&lt;/p&gt;

&lt;p&gt;Gemini introduced me to integrating generative AI into an actual application.&lt;/p&gt;

&lt;p&gt;The Web Speech API showed me how powerful browser capabilities can be for voice interfaces.&lt;/p&gt;

&lt;p&gt;And debugging all of these systems together taught me perhaps the most important lesson:&lt;/p&gt;

&lt;p&gt;Building real products involves solving problems that don't appear in tutorials.&lt;/p&gt;

&lt;p&gt;What's Next for Titan?&lt;/p&gt;

&lt;p&gt;Titan is still a work in progress.&lt;/p&gt;

&lt;p&gt;Some of the things I want to explore next include:&lt;/p&gt;

&lt;p&gt;Better conversational memory&lt;br&gt;
More personalized workout guidance&lt;br&gt;
Improved voice interaction&lt;br&gt;
Better multilingual support&lt;br&gt;
Fitness progress tracking&lt;br&gt;
More natural AI conversations&lt;br&gt;
More advanced 3D character interactions&lt;br&gt;
Better mobile experience&lt;/p&gt;

&lt;p&gt;The long-term goal is to turn Titan from an AI feature into a complete AI fitness experience.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;What started as a fitness website became an experiment in combining AI, 3D graphics, voice interaction and modern web development.&lt;/p&gt;

&lt;p&gt;I'm building these projects under ASForge, where my goal is to explore and create practical AI-powered technology.&lt;/p&gt;

&lt;p&gt;Titan is just one step in that journey.&lt;/p&gt;

&lt;p&gt;If you're also building something with React, AI or Three.js, I'd love to hear about it.&lt;/p&gt;

&lt;p&gt;Keep building. Keep learning. Keep experimenting. 🚀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>react</category>
      <category>javascript</category>
      <category>threejs</category>
    </item>
  </channel>
</rss>
