About this snippet

Recreate the ChatGPT token streaming effect perfectly. The glowing cursor tracks every word dynamically as it renders, giving your AI apps an incredibly authentic look.

Size
4.5 KB
Uses
HTML, CSS, JavaScript
Loads
Nothing external
Views
137
<div class="ai-stream-container">
  <div class="ai-header">
    <div class="ai-avatar">
      <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#ffffff" stroke-width="2"><path d="M12 2a10 10 0 1 0 10 10H12V2z"></path><path d="M12 12 2.1 7.1"></path><path d="M12 12l9.9 4.9"></path></svg>
    </div>
    <span>AI Assistant</span>
  </div>
  
  <div class="ai-response">
    <!-- The raw text is hidden, JS will read it and stream it back -->
    <div id="raw-content" style="display: none;">The future of artificial intelligence is seamlessly integrated into our daily workflows. We are building systems that understand context, generate stunning user interfaces, and perform complex logic flawlessly.</div>
    <div id="stream-output" class="stream-output"></div>
  </div>
  
  <button id="replay-btn" class="replay-btn">Generate Response</button>
</div>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
  background: #0f172a;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 20px;
}

.ai-stream-container {
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 16px;
  width: 100%;
  max-width: 600px;
  padding: 30px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}

.ai-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  color: #f8fafc;
  font-weight: 600;
  font-size: 1.1rem;
}

.ai-avatar {
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: flex;
  justify-content: center;
  align-items: center;
  box-shadow: 0 4px 10px rgba(59, 130, 246, 0.4);
}

.ai-response {
  background: rgba(15, 23, 42, 0.5);
  border-radius: 12px;
  padding: 24px;
  min-height: 120px;
}

.stream-output {
  color: #cbd5e1;
  font-size: 1.1rem;
  line-height: 1.7;
}

/* The glowing cursor automatically sits at the end of the injected text */
.stream-output::after {
  content: "";
  display: inline-block;
  width: 8px;
  height: 1.1rem;
  background: #3b82f6;
  vertical-align: middle;
  margin-left: 2px;
  border-radius: 2px;
  box-shadow: 0 0 8px #3b82f6;
  animation: blink 0.8s infinite;
  opacity: 0;
}

.stream-output.is-streaming::after {
  opacity: 1;
  animation: none;
}

@keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

.token-word {
  opacity: 0;
  display: inline-block;
  transform: translateY(4px);
  /* This margin-right perfectly replaces the collapsed space character */
  margin-right: 0.3em;
  transition: opacity 0.1s, transform 0.1s;
}

.token-word.visible {
  opacity: 1;
  transform: translateY(0);
}

.replay-btn {
  margin-top: 24px;
  background: transparent;
  color: #94a3b8;
  border: 1px solid #475569;
  padding: 10px 20px;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}

.replay-btn:hover {
  background: #334155;
  color: #f8fafc;
}
const rawContentElement = document.getElementById("raw-content");
const output = document.getElementById("stream-output");
const replayBtn = document.getElementById("replay-btn");

let streamInterval;

function startStreaming() {
  /* Clear previous content and reset state */
  output.innerHTML = "";
  output.classList.add("is-streaming");
  clearInterval(streamInterval);
  
  /* Create a queue of words without using array bracket indexing */
  const rawText = rawContentElement.innerText;
  const wordsQueue = rawText.split(" ");
  
  /* Stream tokens sequentially by injecting elements dynamically */
  streamInterval = setInterval(function() {
    if (wordsQueue.length > 0) {
      /* Extract the very first word from the array queue securely */
      const word = wordsQueue.shift();
      
      const span = document.createElement("span");
      span.className = "token-word";
      span.innerText = word;
      
      /* Appending forces the CSS ::after cursor to constantly move forward */
      output.appendChild(span);
      
      /* Trigger reflow to animate the fade-in seamlessly */
      setTimeout(function() {
        span.classList.add("visible");
      }, 10);
      
    } else {
      /* Queue is empty, finish generation */
      clearInterval(streamInterval);
      output.classList.remove("is-streaming");
    }
  }, 50); /* 50ms per token creates a realistic AI pacing */
}

replayBtn.addEventListener("click", startStreaming);

/* Start automatically on page load */
setTimeout(startStreaming, 500);
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save