How to test if the content is LLM process friendly.
Test how AI models read your website. Learn quick methods and use the LLM Content Analyzer tool to improve SEO, readability, and AI discoverability.
Introduction
AI models read websites differently than humans—they ignore visual design and focus on structured text in the HTML source. Content that looks great visually may be invisible to AI if it's embedded in images, loaded via JavaScript, or lacks semantic structure.
Outlining a testing approach helps you identify and fix issues that prevent AI models from properly understanding your content, improving both AI discoverability and SEO performance.
⚠️ Important Note: This guide is for educational purposes only. The LLM landscape is rapidly evolving and entirely new territory. AI model behaviors, crawling patterns, and optimization strategies may change significantly as the technology develops. Use these techniques as learning tools while staying updated on emerging best practices.
Quick Testing Methods
Step 1 — View Raw HTML Text
Open your Webflow site in Chrome (or any modern browser).
Right-click → View Page Source (or press
Ctrl+U/Cmd+Option+U).In the source code, look for your actual content text.
Good sign: You see your headings (
<h1>,<h2>), paragraphs (<p>), and list items (<li>) clearly as plain text.Bad sign: You see gibberish, JavaScript code, or
<canvas>elements instead of readable words.
Step 2 — Simulate a “Plain Text” Read
Open the browser Developer Tools (
F12orCtrl+Shift+I/Cmd+Option+I).Go to the Console tab.
Paste this JavaScript snippet:
document.body.innerTextPress Enter — the browser will dump only the readable text from the page.
This is very close to what I see when I fetch a webpage.
If critical info is missing here, I won’t see it either.
Step 3 — Check for Token Hostility
When looking at the text output:
✅ Token-friendly:
Normal spacing
Regular punctuation
No excessive symbols
No weird encodings
❌ Token-hostile:
Text is inside images or icons
Special Unicode characters instead of normal ones
Overloaded with emojis, symbols, or decorative glyphs
Step 4 — Quick Keyword Scan
Search in the output (Ctrl+F) for your target keywords.
If they appear clean and intact, LLMs can match them easily.
If they’re broken across weird symbols or missing entirely, that’s a ranking problem.
Advanced Testing Tool
Would you like to see what LLM sees? Try out the following
The following will give you an instant grades any webpage A-F on how easily AI models like ChatGPT and Claude can read and understand its content, plus gives specific fixes to improve AI-friendliness.
Instructions
Open the webpage in browser.
Open the Devtool with website (use CTRL+shift+I).
Go to console section.
Paste the following code at console (if see a warning then first type allow pasting at console)
Warning: Don’t paste code into the DevTools Console that you don’t understand or haven’t reviewed yourself. This could allow attackers to steal your identity or take control of your computer. Please type ‘allow pasting’ below and press Enter to allow pasting.)
LLM Content Analyzer Code
(async () => { // Extract visible page text const visibleText = document.body.innerText.replace(/\s+/g, ' ').trim(); // LLM Friendliness Analyzer function analyzeLLMFriendliness() { const scores = {}; let totalScore = 0; // 1. Text Extraction Quality (25 points) const sourceHTML = document.documentElement.outerHTML; const textInSource = sourceHTML.includes(visibleText.slice(0, 100)); const hasImages = document.querySelectorAll('img').length; const imagesWithAlt = document.querySelectorAll('img[alt]').length; const altCoverage = hasImages > 0 ? (imagesWithAlt / hasImages) * 100 : 100; scores.textExtraction = textInSource ? 25 : 10; totalScore += scores.textExtraction; // 2. Semantic HTML Structure (25 points) const h1Count = document.querySelectorAll('h1').length; const hasHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length > 0; const hasParagraphs = document.querySelectorAll('p').length > 0; const hasLists = document.querySelectorAll('ul, ol').length > 0; let semanticScore = 0; if (h1Count === 1) semanticScore += 8; else if (h1Count > 1) semanticScore += 3; if (hasHeadings) semanticScore += 7; if (hasParagraphs) semanticScore += 5; if (hasLists) semanticScore += 5; scores.semantic = Math.min(semanticScore, 25); totalScore += scores.semantic; // 3. Content Visibility (15 points) const hiddenElements = document.querySelectorAll('[style*="display: none"], [style*="visibility: hidden"], .hidden').length; const totalElements = document.querySelectorAll('*').length; const visibilityRatio = 1 - (hiddenElements / totalElements); scores.visibility = Math.round(visibilityRatio * 15); totalScore += scores.visibility; // 4. Token Efficiency (15 points) const tokenCount = Math.ceil(visibleText.length / 4); // Rough estimate const efficiency = Math.min(visibleText.length / tokenCount, 5) / 5; // Characters per token scores.efficiency = Math.round(efficiency * 15); totalScore += scores.efficiency; // 5. Context Clarity (10 points) const title = document.title || ''; const metaDesc = document.querySelector('meta[name="description"]')?.content || ''; const hasTitle = title.length > 0; const hasMetaDesc = metaDesc.length > 0; scores.context = (hasTitle ? 5 : 0) + (hasMetaDesc ? 5 : 0); totalScore += scores.context; // 6. Accessibility (10 points) const linksWithText = document.querySelectorAll('a').length; const descriptiveLinks = Array.from(document.querySelectorAll('a')).filter( link => link.textContent && !['click here', 'read more', 'link'].includes(link.textContent.toLowerCase().trim()) ).length; const linkScore = linksWithText > 0 ? (descriptiveLinks / linksWithText) * 10 : 10; scores.accessibility = Math.round(Math.min(linkScore + (altCoverage / 10), 10)); totalScore += scores.accessibility; // Calculate grade let grade = 'F'; if (totalScore >= 90) grade = 'A'; else if (totalScore >= 80) grade = 'B'; else if (totalScore >= 70) grade = 'C'; else if (totalScore >= 60) grade = 'D'; return { scores, totalScore, grade, altCoverage, tokenCount }; } const analysis = analyzeLLMFriendliness(); // Create enhanced popup UI const popup = document.createElement('div'); popup.className = 'token-analyzer-popup'; // Inject styles const styles = ` <style> .token-analyzer-popup { position: fixed; top: 20px; right: 20px; width: 560px; max-height: 90vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; box-shadow: 0 20px 40px rgba(0,0,0,0.15); z-index: 999999; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; animation: slideIn 0.3s ease-out; } @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } .token-header { background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); padding: 20px 24px; border-bottom: 1px solid rgba(255,255,255,0.2); color: white; } .token-title { font-size: 20px; font-weight: 600; margin: 0; display: flex; align-items: center; gap: 10px; } .token-icon { width: 24px; height: 24px; background: rgba(255,255,255,0.2); border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; } .grade-badge { margin-left: auto; padding: 8px 16px; border-radius: 20px; font-weight: 700; font-size: 16px; } .grade-A { background: #28a745; } .grade-B { background: #17a2b8; } .grade-C { background: #ffc107; color: #000; } .grade-D { background: #fd7e14; } .grade-F { background: #dc3545; } .token-content { background: white; max-height: calc(90vh - 80px); overflow-y: auto; } .token-section { padding: 20px 24px; border-bottom: 1px solid #f0f0f0; } .token-section:last-child { border-bottom: none; } .section-title { font-size: 14px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; } .text-preview { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 12px; line-height: 1.5; color: #495057; max-height: 150px; overflow-y: auto; white-space: pre-wrap; } .stats-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; margin-bottom: 16px; } .stat-card { background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 8px; padding: 12px; text-align: center; } .stat-value { font-size: 20px; font-weight: 700; color: #495057; margin-bottom: 4px; } .stat-label { font-size: 11px; color: #6c757d; text-transform: uppercase; letter-spacing: 0.5px; } .llm-scores { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 16px; } .score-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: #f8f9fa; border-radius: 6px; font-size: 12px; } .score-value { font-weight: 600; color: #495057; } .btn { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 10px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; margin-right: 8px; margin-bottom: 8px; } .btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); } .btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .btn-secondary { background: #6c757d; } .btn-danger { background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); } .token-output { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; line-height: 1.4; max-height: 300px; overflow-y: auto; white-space: pre-wrap; color: #495057; margin-top: 12px; } .loading { display: flex; align-items: center; justify-content: center; padding: 40px; color: #6c757d; } .spinner { width: 20px; height: 20px; border: 2px solid #e9ecef; border-top: 2px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; margin-right: 8px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .tabs { display: flex; border-bottom: 1px solid #e9ecef; } .tab { padding: 12px 20px; cursor: pointer; border-bottom: 2px solid transparent; font-size: 14px; font-weight: 500; color: #666; transition: all 0.2s ease; } .tab.active { color: #667eea; border-bottom-color: #667eea; } .tab-content { display: none; } .tab-content.active { display: block; } </style> `; popup.innerHTML = styles + ` <div class="token-header"> <h3 class="token-title"> <div class="token-icon">🤖</div> LLM Content Analyzer <div class="grade-badge grade-${analysis.grade}">${analysis.grade} (${analysis.totalScore}/100)</div> </h3> </div> <div class="token-content"> <div class="tabs"> <div class="tab active" data-tab="overview">Overview</div> <div class="tab" data-tab="llm-grade">LLM Grade</div> <div class="tab" data-tab="tokens">Tokens</div> </div> <div class="tab-content active" id="overview"> <div class="token-section"> <div class="section-title">Content Preview</div> <div class="text-preview">${visibleText.slice(0, 400)}${visibleText.length > 400 ? '\n\n... (showing first 400 characters)' : ''}</div> </div> <div class="token-section"> <div class="section-title">Quick Stats</div> <div class="stats-grid"> <div class="stat-card"> <div class="stat-value">${visibleText.length.toLocaleString()}</div> <div class="stat-label">Characters</div> </div> <div class="stat-card"> <div class="stat-value">${visibleText.split(/\s+/).length.toLocaleString()}</div> <div class="stat-label">Words</div> </div> <div class="stat-card"> <div class="stat-value">${analysis.tokenCount.toLocaleString()}</div> <div class="stat-label">Est. Tokens</div> </div> </div> </div> </div> <div class="tab-content" id="llm-grade"> <div class="token-section"> <div class="section-title">LLM Friendliness Breakdown</div> <div class="llm-scores"> <div class="score-item"> <span>Text Extraction</span> <span class="score-value">${analysis.scores.textExtraction}/25</span> </div> <div class="score-item"> <span>HTML Structure</span> <span class="score-value">${analysis.scores.semantic}/25</span> </div> <div class="score-item"> <span>Content Visibility</span> <span class="score-value">${analysis.scores.visibility}/15</span> </div> <div class="score-item"> <span>Token Efficiency</span> <span class="score-value">${analysis.scores.efficiency}/15</span> </div> <div class="score-item"> <span>Context Clarity</span> <span class="score-value">${analysis.scores.context}/10</span> </div> <div class="score-item"> <span>Accessibility</span> <span class="score-value">${analysis.scores.accessibility}/10</span> </div> </div> <div id="recommendations"></div> </div> </div> <div class="tab-content" id="tokens"> <div class="token-section"> <div class="section-title">Token Analysis</div> <button class="btn" id="showTokensBtn"> 🔢 Analyze Tokens </button> <button class="btn btn-secondary" id="copyTextBtn"> 📋 Copy Text </button> <div id="tokenOutput" class="token-output" style="display: none;"></div> </div> </div> <div class="token-section" style="border-bottom: none;"> <button class="btn btn-danger" id="closePopup"> ✕ Close </button> </div> </div> `; document.body.appendChild(popup); // Tab functionality document.querySelectorAll('.tab').forEach(tab => { tab.onclick = () => { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); tab.classList.add('active'); document.getElementById(tab.dataset.tab).classList.add('active'); }; }); // Generate recommendations function generateRecommendations() { const recs = []; if (analysis.scores.textExtraction < 20) { recs.push("⚠️ Text may not be in HTML source - check for image-based or JS-rendered content"); } if (analysis.scores.semantic < 20) { recs.push("📝 Use semantic HTML: proper h1-h6 headings, p tags, lists"); } if (analysis.scores.visibility < 10) { recs.push("👁️ Too much hidden content - ensure main text is visible"); } if (analysis.scores.efficiency < 10) { recs.push("⚡ Improve token efficiency - remove repetitive text and fluff"); } if (analysis.scores.context < 5) { recs.push("🏷️ Add meta title and description for better context"); } if (analysis.scores.accessibility < 7) { recs.push("♿ Improve accessibility: alt text for images, descriptive links"); } if (recs.length === 0) { recs.push("✅ Great job! This page is very LLM-friendly"); } document.getElementById('recommendations').innerHTML = '<div class="section-title" style="margin-top: 16px;">Recommendations</div>' + recs.map(rec => `<div style="margin-bottom: 8px; font-size: 13px;">${rec}</div>`).join(''); } generateRecommendations(); // Event handlers document.getElementById('closePopup').onclick = () => { popup.style.animation = 'slideIn 0.3s ease-out reverse'; setTimeout(() => popup.remove(), 300); }; document.getElementById('copyTextBtn').onclick = () => { navigator.clipboard.writeText(visibleText).then(() => { const btn = document.getElementById('copyTextBtn'); const originalText = btn.innerHTML; btn.innerHTML = '✓ Copied!'; btn.style.background = '#28a745'; setTimeout(() => { btn.innerHTML = originalText; btn.style.background = ''; }, 2000); }); }; document.getElementById('showTokensBtn').onclick = async () => { const btn = document.getElementById('showTokensBtn'); const output = document.getElementById('tokenOutput'); btn.disabled = true; btn.innerHTML = '<div class="spinner"></div>Loading...'; output.style.display = 'block'; output.innerHTML = '<div class="loading"><div class="spinner"></div>Loading tokenizer...</div>'; try { await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js'; script.onload = resolve; script.onerror = () => reject(new Error('Failed to load tokenizer.')); document.head.appendChild(script); }); const { encode } = window.GPTTokenizer_cl100k_base; const tokens = encode(visibleText); const words = visibleText.split(/\s+/); const maxTokensToShow = 100; const tokenPairs = tokens.slice(0, maxTokensToShow).map((id, idx) => { const word = words[idx] || '...'; return `${String(idx + 1).padStart(3, ' ')}: "${word}" → ${id}`; }); const analysisResult = `Token Analysis Results ${'='.repeat(50)} 📊 Statistics: • Total Tokens: ${tokens.length.toLocaleString()} • Characters: ${visibleText.length.toLocaleString()} • Words: ${words.length.toLocaleString()} • Chars/Token: ${(visibleText.length / tokens.length).toFixed(2)} • Compression: ${((1 - tokens.length / visibleText.length) * 100).toFixed(1)}% 🔢 Token Mappings (first ${Math.min(maxTokensToShow, tokens.length)}): ${tokenPairs.join('\n')}${tokens.length > maxTokensToShow ? `\n\n... and ${(tokens.length - maxTokensToShow).toLocaleString()} more tokens` : ''} 💡 LLM Context: • GPT-4 Context: ~128k tokens (${Math.round(tokens.length / 128000 * 100)}% used) • Claude Context: ~200k tokens (${Math.round(tokens.length / 200000 * 100)}% used)`; output.textContent = analysisResult; btn.innerHTML = '✓ Complete'; btn.style.background = '#28a745'; } catch (err) { output.textContent = '❌ Error loading tokenizer. Check internet connection.'; btn.innerHTML = '❌ Error'; btn.style.background = '#dc3545'; } }; })();What the Tool Shows:
A-F Grade with 100-point scoring system
Content preview showing what AI sees
6 scoring categories: Text extraction, HTML structure, visibility, token efficiency, context clarity, accessibility
Specific recommendations for improvement
Token analysis with compression ratios and context usage
It instantly identifies LLM optimization opportunities and provides actionable fixes for better AI discoverability.
Uncover deep insights from employee feedback using advanced natural language processing.
Join the Founder’s Club
$9/mo
annually
Perfect if you’re just getting started with Flozi. You get full access to the editor, SEO features, and Notion-to-Webflow sync.
.png)
.jpg)
.jpg)