DEV Community

Tom Yahav
Tom Yahav

Posted on

1

How to Build an AI Avatar Chatbot in Your Frontend App

Introduction & Context

AI chatbots are no longer just functional—they can be expressive, voice-enabled, and visually engaging. In this tutorial, you'll learn how to build an AI-powered chatbot in a Vue 3 app that not only talks like ChatGPT, but also has a visual avatar that reacts to the conversation.

This is perfect for use cases like virtual tutors, onboarding assistants, storytelling characters, and customer service bots.

Goals and What You’ll Learn

By the end of this article, you’ll know how to:

  • Integrate OpenAI’s GPT model for conversational logic
  • Build a frontend chat UI with avatars and dynamic expressions
  • Use the Web Speech API for text-to-speech (TTS)
  • Animate avatar emotions based on message sentiment
  • Enhance UX for accessibility and user delight

Project Overview

We’ll build:

  • A chat interface
  • An animated avatar (using CSS or Lottie)
  • A voice synthesizer to read AI replies

Tech Stack

  • Vue 3 + Composition API
  • OpenAI GPT API (gpt-3.5-turbo)
  • Web Speech API (TTS)
  • Tailwind CSS or basic CSS for styling
  • Optional: Lottie animations for expressive avatars

Step 1: Setup Your Vue 3 Project

npm create vite@latest ai-avatar-chatbot -- --template vue
cd ai-avatar-chatbot
npm install
npm install axios
Enter fullscreen mode Exit fullscreen mode

Add your API key to .env:

VITE_OPENAI_API_KEY=your_openai_api_key_here
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Chat Interface

<script setup>
import { ref, watch, nextTick } from 'vue'
import axios from 'axios'
import LottieAvatar from './LottieAvatar.vue'
const userInput = ref('')
const messages = ref([])
const chatContainer = ref(null)
const lottieAvatar = ref(null)

const speak = (text) => {
  const synth = window.speechSynthesis
  const utter = new SpeechSynthesisUtterance(text)

  lottieAvatar.value?.play()

  utter.onend = () => {
    lottieAvatar.value?.stop()
  }

  utter.onerror = (event) => {
    console.error('SpeechSynthesisUtterance.onerror', event)
    lottieAvatar.value?.stop()
  }

  synth.speak(utter)
}

const sendMessage = async () => {
  if (!userInput.value.trim()) return
  const prompt = userInput.value
  messages.value.push({ role: 'user', content: prompt })
  userInput.value = ''

  try {
    const res = await axios.post(
      'https://api.openai.com/v1/chat/completions',
      {
        model: 'gpt-3.5-turbo',
        messages: [...messages.value, { role: 'user', content: prompt }]
      },
      {
        headers: {
          Authorization: `Bearer ${import.meta.env.VITE_OPENAI_API_KEY}`
        }
      }
    )

    const aiReply = res.data.choices[0].message.content
    messages.value.push({ role: 'assistant', content: aiReply })
    speak(aiReply)
    await nextTick()
    chatContainer.value.scrollTop = chatContainer.value.scrollHeight
  } catch (err) {
    console.error('Error sending message:', err)
    messages.value.push({ role: 'assistant', content: 'Oops, something went wrong.' })
    lottieAvatar.value?.stop()
  }
}
</script>

<template>
  <div class="max-w-2xl mx-auto p-6 sm:p-8 bg-white rounded-lg shadow-md">
    <div class="flex items-center gap-4 mb-8 border-b pb-6">
      <LottieAvatar ref="lottieAvatar" />
      <div class="text-2xl font-semibold text-gray-700">AI Assistant</div>
    </div>

    <div class="h-80 overflow-y-auto mb-6 p-5 space-y-5 bg-gray-50 rounded-md" ref="chatContainer">
      <div v-for="(msg, i) in messages" :key="i" class="flex" :class="{'justify-end': msg.role === 'user', 'justify-start': msg.role === 'assistant'}">
        <div
          class="max-w-xs lg:max-w-md px-4 py-2 rounded-lg shadow"
          :class="{
            'bg-blue-500 text-white': msg.role === 'user',
            'bg-gray-200 text-gray-800': msg.role === 'assistant'
          }"
        >
          {{ msg.content }}
        </div>
      </div>
    </div>

    <div class="flex gap-4 items-end">
      <textarea
        v-model="userInput"
        @keyup.enter.prevent="sendMessage"
        class="flex-grow mt-1 p-3 bg-gray-50 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent resize-none shadow-sm transition duration-150 ease-in-out"
        placeholder="Ask me anything..."
        rows="3"
      />
      <button
        @click="sendMessage"
        class="mt-1 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-semibold py-3 px-5 rounded-lg transition duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 shadow-md disabled:opacity-60 disabled:cursor-not-allowed h-[4.75rem] flex items-center justify-center"
        :disabled="!userInput.trim()"
      >
        <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707l-3-3a1 1 0 00-1.414 1.414L10.586 9H7a1 1 0 100 2h3.586l-1.293 1.293a1 1 0 101.414 1.414l3-3a1 1 0 000-1.414z" clip-rule="evenodd" />
        </svg>
        <span class="ml-2">Send</span>
      </button>
    </div>
  </div>
</template>

<style scoped>
/* Add custom scrollbar styles for WebKit browsers (Chrome, Safari) */
.overflow-y-auto::-webkit-scrollbar {
  width: 8px;
}

.overflow-y-auto::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 10px;
}

.overflow-y-auto::-webkit-scrollbar-thumb {
  background: #d1d5db; /* Tailwind gray-400 */
  border-radius: 10px;
}

.overflow-y-auto::-webkit-scrollbar-thumb:hover {
  background: #9ca3af; /* Tailwind gray-500 */
}

/* Add some basic scrollbar styles for Firefox */
.overflow-y-auto {
  scrollbar-width: thin;
  scrollbar-color: #d1d5db #f1f1f1; /* thumb track */
}
</style>

Enter fullscreen mode Exit fullscreen mode

Optional: Use Lottie for Avatar Animation

To add expressive animations instead of static avatar images, you can use Lottie, a lightweight animation library.

Step 1: Install lottie-web
npm install lottie-web

Step 2: Download an animation JSON file
Get a character animation from lottiefiles.com, for example a talking face or blinking robot. Place it under public/animations/ai-avatar.json.

Step 3: Create a reusable LottieAvatar.vue component

<template>
  <div ref="container" class="w-24 h-24" />
</template>

<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import lottie from 'lottie-web'

const container = ref(null)
let animationInstance

onMounted(() => {
  animationInstance = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    path: '/animations/ai-avatar.json'
  })
})

onUnmounted(() => {
  if (animationInstance) animationInstance.destroy()
})
</script>
Enter fullscreen mode Exit fullscreen mode

Step 4: Replace the static image in your main chat UI

<!-- Instead of using an <img> tag -->
<LottieAvatar />

<script setup>
import LottieAvatar from '@/components/LottieAvatar.vue'
</script>
Enter fullscreen mode Exit fullscreen mode

Optional: Dynamically switch Lottie animations
If you want to show different moods:

watch(mood, (newMood) => {
  animationInstance?.destroy()
  animationInstance = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    path: `/animations/${newMood}.json`
  })
})
Enter fullscreen mode Exit fullscreen mode

Now your chatbot can visually react using Lottie-based animations!

Use a library like lottie-web to play animations like nodding, blinking, or reacting.
npm install lottie-web
Then embed dynamic animations instead of static images.

Accessibility & UX Enhancements

  • Use aria-live="polite" on the chat area
  • Add keyboard accessibility for sending messages (Enter key)
  • Use voice only when user allows (ask permission)

💡 Tip: Let users switch avatars or voices to personalize their AI companion!

Links and References

Summary and Conclusion

You’ve just built an intelligent, voice-capable, expressive AI chatbot—right inside your Vue 3 frontend. This component can become anything: a tutor, a support agent, a character in a story, or even a creative writing partner.

By blending AI + animation + voice, you unlock a whole new dimension of web experience.

Call to Action / Community Engagement

What personality would you give your AI bot? How would you theme or brand the avatar experience? Share your AI chatbot creations or ask questions in the comments!

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Survey image

Calling All Cloud Developers - Your Insights Matter

Take the Developer Nation Survey and help shape cloud development trends. Prizes await!

Join Today