How I Built a 'Living' AI Chatbot with Next.js, Mistral, and Framer Motion

Published: (February 18, 2026 at 11:50 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

🚀 The Introduction

Most AI chatbots are boring. They are usually just a static box in the corner of the screen. When I was building the AI assistant for Trainlytic, I wanted something different—a bot that felt “alive,” an assistant that acknowledges your presence before you even start typing.

🤖 The Vision: A Bot with Personality

I didn’t want a “wrapper.” I wanted a UI that felt like the year 3026. The goal was to combine glassmorphism, neon accents, and interactive animation to create a high‑fidelity user experience.

Key Features

  • Cursor Tracking: Eyes follow the user’s mouse movement.
  • Natural Blinking: Randomized double‑blinks to mimic biological behavior.
  • Mistral AI Integration: Specialized fitness and nutrition coaching.

Close‑up of the Trainlytic AI interactive robot face featuring glowing emerald eyes and a metallic visor

👁️ The “Eye” Logic: Mathematical Cursor Tracking

The standout feature is the eye‑tracking. The eyes calculate the distance between the cursor and the center of the bot’s face, limiting movement to a natural range.

// Cursor tracking for eyes
useEffect(() => {
  const handleMouseMove = (e: MouseEvent) => {
    if (!robotRef.current || isOpen) return;

    const rect = robotRef.current.getBoundingClientRect();
    const robotCenterX = rect.left + rect.width / 2;
    const robotCenterY = rect.top + rect.height / 2;

    const deltaX = e.clientX - robotCenterX;
    const deltaY = e.clientY - robotCenterY;

    // Limit eye movement range (max 4px offset for natural movement)
    const maxOffset = 4;
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    const normalizedDistance = Math.min(distance / 300, 1);

    const offsetX = (deltaX / (Math.abs(deltaX) + 100)) * maxOffset * normalizedDistance;
    const offsetY = (deltaY / (Math.abs(deltaY) + 100)) * maxOffset * normalizedDistance;

    setEyeOffset({ x: offsetX, y: offsetY });
  };

  window.addEventListener("mousemove", handleMouseMove);
  return () => window.removeEventListener("mousemove", handleMouseMove);
}, [isOpen]);

⚡ The Brain: Mistral AI & Streaming

For the “brain,” I chose Mistral AI. It’s fast and well‑suited for the fitness and nutrition prompts I use to help users reach their goals. To avoid a clunky experience, I implemented streaming responses so the text flows into the chat bubble as it arrives.

The full Trainlytic AI chat interface showing glassmorphism design, neon accents, and a fitness coaching conversation

🎨 UI/UX: Natural Blinking

A performDoubleBlink function triggers at random intervals, giving the bot a more lifelike presence.

const performDoubleBlink = () => {
  setIsBlinking(true); // First blink
  setTimeout(() => {
    setIsBlinking(false);
    setTimeout(() => {
      setIsBlinking(true); // Second rapid blink
      setTimeout(() => setIsBlinking(false), 100);
    }, 150);
  }, 100);
};

💡 Conclusion

Building this taught me that UX engineering is just as important as the AI itself. People stay on the page longer just to play with the bot’s eyes!

Check it out live at Trainlytic.net and let me know what you think of the design in the comments!

0 views
Back to Blog

Related posts

Read more »

Apex B. OpenClaw, Local Embeddings.

Local Embeddings para Private Memory Search Por default, el memory search de OpenClaw envía texto a un embedding API externo típicamente Anthropic u OpenAI par...