Spaces:
Running
Running
| <html> | |
| <head> | |
| <title>SVG Transcript Time Series with Teleprompter</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 20px; | |
| font-family: Arial, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| overflow-x: hidden; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .teleprompter { | |
| background: rgba(0,0,0,0.8); | |
| border-radius: 15px; | |
| padding: 30px; | |
| margin-bottom: 30px; | |
| backdrop-filter: blur(10px); | |
| border: 2px solid rgba(255,255,255,0.1); | |
| } | |
| .teleprompter-text { | |
| font-size: 24px; | |
| line-height: 1.6; | |
| text-align: center; | |
| min-height: 120px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .timestamp { | |
| color: #ff6b6b; | |
| font-weight: bold; | |
| font-size: 28px; | |
| margin-bottom: 10px; | |
| } | |
| .current-text { | |
| opacity: 1; | |
| transition: opacity 0.5s ease; | |
| } | |
| .highlighted-word { | |
| background: linear-gradient(45deg, #ff6b6b, #ffa500); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| animation: pulse 1s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| } | |
| .chart-container { | |
| background: rgba(255,255,255,0.95); | |
| border-radius: 15px; | |
| padding: 30px; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.1); | |
| } | |
| .cluster-legend { | |
| display: flex; | |
| justify-content: center; | |
| gap: 30px; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| } | |
| .legend-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-weight: bold; | |
| color: #333; | |
| } | |
| .legend-color { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| } | |
| .controls { | |
| text-align: center; | |
| margin: 20px 0; | |
| } | |
| button { | |
| background: linear-gradient(45deg, #667eea, #764ba2); | |
| border: none; | |
| color: white; | |
| padding: 12px 24px; | |
| margin: 0 10px; | |
| border-radius: 25px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| transition: transform 0.2s; | |
| } | |
| button:hover { | |
| transform: translateY(-2px); | |
| } | |
| button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 6px; | |
| background: rgba(255,255,255,0.3); | |
| border-radius: 3px; | |
| margin: 20px 0; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #ff6b6b, #ffa500); | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| border-radius: 3px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="teleprompter"> | |
| <div class="timestamp" id="timestamp">5:48</div> | |
| <div class="teleprompter-text"> | |
| <div class="current-text" id="current-text"> | |
| It's a very good way of understanding how we communicate <span class="highlighted-word">linguistically</span>. | |
| </div> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progress-fill"></div> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <button onclick="togglePlayPause()" id="playButton">βΈοΈ Pause</button> | |
| <button onclick="restart()">π Restart</button> | |
| <button onclick="changeSpeed(-0.5)">π Slower</button> | |
| <button onclick="changeSpeed(0.5)">π° Faster</button> | |
| </div> | |
| <div class="chart-container"> | |
| <div class="cluster-legend"> | |
| <div class="legend-item"> | |
| <div class="legend-color" style="background: #ff4757;"></div> | |
| <span>ποΈ Vision/Perception</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-color" style="background: #2ed573;"></div> | |
| <span>πͺ Action</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-color" style="background: #3742fa;"></div> | |
| <span>π€ Thought</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-color" style="background: #ffa502;"></div> | |
| <span>π Context</span> | |
| </div> | |
| </div> | |
| <svg id="chart" width="100%" height="400" viewBox="0 0 800 400"> | |
| <!-- Background grid --> | |
| <defs> | |
| <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse"> | |
| <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#e0e0e0" stroke-width="1"/> | |
| </pattern> | |
| <!-- Gradients for lines --> | |
| <linearGradient id="visionGradient" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" style="stop-color:#ff4757;stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:#ff4757;stop-opacity:1" /> | |
| </linearGradient> | |
| <linearGradient id="actionGradient" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" style="stop-color:#2ed573;stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:#2ed573;stop-opacity:1" /> | |
| </linearGradient> | |
| <linearGradient id="thoughtGradient" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" style="stop-color:#3742fa;stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:#3742fa;stop-opacity:1" /> | |
| </linearGradient> | |
| <linearGradient id="contextGradient" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" style="stop-color:#ffa502;stop-opacity:0.3" /> | |
| <stop offset="100%" style="stop-color:#ffa502;stop-opacity:1" /> | |
| </linearGradient> | |
| </defs> | |
| <rect width="800" height="400" fill="url(#grid)" opacity="0.5"/> | |
| <!-- Axes --> | |
| <line x1="80" y1="50" x2="80" y2="350" stroke="#333" stroke-width="2"/> | |
| <line x1="80" y1="350" x2="720" y2="350" stroke="#333" stroke-width="2"/> | |
| <!-- Y-axis labels --> | |
| <text x="70" y="350" text-anchor="end" font-size="12" fill="#666">0</text> | |
| <text x="70" y="290" text-anchor="end" font-size="12" fill="#666">1</text> | |
| <text x="70" y="230" text-anchor="end" font-size="12" fill="#666">2</text> | |
| <text x="70" y="170" text-anchor="end" font-size="12" fill="#666">3</text> | |
| <text x="70" y="110" text-anchor="end" font-size="12" fill="#666">4</text> | |
| <text x="70" y="50" text-anchor="end" font-size="12" fill="#666">5</text> | |
| <!-- Axis labels --> | |
| <text x="400" y="390" text-anchor="middle" font-size="14" font-weight="bold" fill="#333">Time (MM:SS)</text> | |
| <text x="30" y="200" text-anchor="middle" font-size="14" font-weight="bold" fill="#333" transform="rotate(-90 30 200)">Word Frequency</text> | |
| <!-- Time markers will be added by JavaScript --> | |
| <g id="time-markers"></g> | |
| <!-- Chart lines --> | |
| <g id="chart-lines"></g> | |
| <!-- Peak labels --> | |
| <g id="peak-labels"></g> | |
| <!-- Current time indicator --> | |
| <line id="time-indicator" x1="80" y1="50" x2="80" y2="350" stroke="#ff6b6b" stroke-width="3" opacity="0.8"> | |
| <animate attributeName="opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/> | |
| </line> | |
| </svg> | |
| </div> | |
| </div> | |
| <script> | |
| // Transcript data with enhanced parsing | |
| const transcriptData = [ | |
| { time: "5:48", seconds: 348, text: "It's a very good way of understanding how we communicate π£οΈlinguisticallyπ£οΈ." }, | |
| { time: "5:55", seconds: 355, text: "So if the words πspringπ to the ποΈvisualποΈ, full ποΈvisualποΈ πcomplexityπ and then that can then πtransformπ itself into πͺactionπͺ." }, | |
| { time: "6:02", seconds: 362, text: "Well those are both relevant and it's an important thing to understand because the classic empiricists make the π€presumptionπ€," }, | |
| { time: "6:10", seconds: 370, text: "and it's an erroneous π€presumptionπ€, that ποΈperceptionποΈ is a value free enterprise." }, | |
| { time: "6:16", seconds: 376, text: "And they π«οΈassumeπ«οΈ that partly because they π€thinkπ€ of ποΈperceptionποΈ as something passive. You know, you just turn your head" }, | |
| { time: "6:21", seconds: 381, text: "and you πlookπ at the πworldπ and there it is. It's like ποΈperceptionποΈ is not passive. There is no ποΈperceptionποΈ without πͺactionπͺ, ever, ever." }, | |
| { time: "6:29", seconds: 389, text: "And that's a weird thing to understand because even when you're πlookingπ at something like your ποΈeyesποΈ are moving back and forth, if they ever stop moving" }, | |
| { time: "6:35", seconds: 395, text: "for a 10th of a second, you stop being able to πseeπ. So your ποΈeyesποΈ are jiggling back and forth, just to keep them active." }, | |
| { time: "6:41", seconds: 401, text: "And then there's involuntary movements of your ποΈeyesποΈ, and then there's voluntary movements of your ποΈeyesποΈ. Like what you're doing with your ποΈeyesποΈ is very much like" }, | |
| { time: "6:48", seconds: 408, text: "what a blind person would do if they were feeling out the contours of a object. You're πsamplingπ and you're only πsamplingπ" }, | |
| { time: "6:56", seconds: 416, text: "a small element of the space that's in front of you. And the element that you πchooseπ" }, | |
| { time: "7:01", seconds: 421, text: "to πsampleπ is dependent on your π―aimsπ― and your π―goalsπ―. So it's value saturated. And so all your ποΈperceptionsποΈ are πͺactionπͺ predicated." }, | |
| { time: "7:09", seconds: 429, text: "And partly what you're doing when you're π£οΈcommunicatingπ£οΈ is therefore not only changing people's πͺactionsπͺ, let's say, but you're also changing the strategy" }, | |
| { time: "7:18", seconds: 438, text: "that they use to ποΈperceiveποΈ. And so you change the way the πworldπ reveals itself for them. See, this is why it's such a profound experience" }, | |
| { time: "7:25", seconds: 445, text: "to read a particularly deep π€thinkerπ€ because you could also π€thinkπ€ of your ποΈperceptionsποΈ" }, | |
| { time: "7:30", seconds: 450, text: "as the axioms of your π€thoughtπ€. That's a good way of π€thinkingπ€ about it. A ποΈperceptionποΈ is like a, what would you say?" }, | |
| { time: "7:36", seconds: 456, text: "It's a π€thoughtπ€ that's so set in concrete that you now πseeπ it rather than conceptualize it." }, | |
| { time: "7:42", seconds: 462, text: "A really profound π€thinkerπ€ changes the way you ποΈperceiveποΈ the πworldπ. That's way deeper than just how you π€thinkπ€ about it" }, | |
| { time: "7:47", seconds: 467, text: "or how you feel about it. What about not just profound π€thinkersπ€, but π€thinkersπ€ that deliver a powerful π‘ideaπ‘?" } | |
| ]; | |
| // Enhanced word extraction from emoji-marked text | |
| function extractWords(text) { | |
| const emojiPatterns = { | |
| vision: /ποΈ([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)ποΈ/g, | |
| action: /πͺ([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)πͺ/g, | |
| thought: /π€([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π€/g, | |
| context: /(?:π£οΈ([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π£οΈ|π([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π|π([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π|π([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π|π―([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π―|π([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π|π‘([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π‘|π«οΈ([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π«οΈ|π([^ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]+)π)/g | |
| }; | |
| const words = { vision: [], action: [], thought: [], context: [] }; | |
| Object.keys(emojiPatterns).forEach(category => { | |
| let match; | |
| while ((match = emojiPatterns[category].exec(text)) !== null) { | |
| const word = match.slice(1).filter(Boolean)[0]; | |
| if (word) { | |
| words[category].push(word.trim()); | |
| } | |
| } | |
| }); | |
| return words; | |
| } | |
| // Process transcript data | |
| const processedData = transcriptData.map(entry => { | |
| const words = extractWords(entry.text); | |
| return { | |
| ...entry, | |
| words, | |
| wordCounts: { | |
| vision: words.vision.length, | |
| action: words.action.length, | |
| thought: words.thought.length, | |
| context: words.context.length | |
| } | |
| }; | |
| }); | |
| // Create time series data | |
| const timePoints = processedData.map(d => d.seconds); | |
| const minTime = Math.min(...timePoints); | |
| const maxTime = Math.max(...timePoints); | |
| // Chart configuration | |
| const chartWidth = 640; | |
| const chartHeight = 300; | |
| const chartLeft = 80; | |
| const chartTop = 50; | |
| const colors = { | |
| vision: '#ff4757', | |
| action: '#2ed573', | |
| thought: '#3742fa', | |
| context: '#ffa502' | |
| }; | |
| // Animation state | |
| let currentIndex = 0; | |
| let isPlaying = true; | |
| let animationSpeed = 1.0; | |
| let animationTimer; | |
| function timeToX(seconds) { | |
| return chartLeft + ((seconds - minTime) / (maxTime - minTime)) * chartWidth; | |
| } | |
| function countToY(count) { | |
| return chartTop + chartHeight - (count * 60); // Scale factor of 60 pixels per count | |
| } | |
| function createTimeSeries() { | |
| const svg = document.getElementById('chart'); | |
| const timeMarkers = document.getElementById('time-markers'); | |
| const chartLines = document.getElementById('chart-lines'); | |
| const peakLabels = document.getElementById('peak-labels'); | |
| // Add time markers | |
| processedData.forEach((entry, index) => { | |
| if (index % 3 === 0) { // Show every 3rd timestamp to avoid crowding | |
| const x = timeToX(entry.seconds); | |
| // Tick mark | |
| const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line'); | |
| tick.setAttribute('x1', x); | |
| tick.setAttribute('y1', chartTop + chartHeight); | |
| tick.setAttribute('x2', x); | |
| tick.setAttribute('y2', chartTop + chartHeight + 5); | |
| tick.setAttribute('stroke', '#666'); | |
| tick.setAttribute('stroke-width', '1'); | |
| timeMarkers.appendChild(tick); | |
| // Label | |
| const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); | |
| label.setAttribute('x', x); | |
| label.setAttribute('y', chartTop + chartHeight + 20); | |
| label.setAttribute('text-anchor', 'middle'); | |
| label.setAttribute('font-size', '10'); | |
| label.setAttribute('fill', '#666'); | |
| label.textContent = entry.time; | |
| timeMarkers.appendChild(label); | |
| } | |
| }); | |
| // Create lines for each category | |
| Object.keys(colors).forEach(category => { | |
| const line = document.createElementNS('http://www.w3.org/2000/svg', 'polyline'); | |
| line.setAttribute('fill', 'none'); | |
| line.setAttribute('stroke', colors[category]); | |
| line.setAttribute('stroke-width', '3'); | |
| line.setAttribute('stroke-linecap', 'round'); | |
| line.setAttribute('stroke-linejoin', 'round'); | |
| line.setAttribute('id', `line-${category}`); | |
| line.style.filter = 'drop-shadow(2px 2px 4px rgba(0,0,0,0.3))'; | |
| chartLines.appendChild(line); | |
| // Area under curve | |
| const area = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); | |
| area.setAttribute('fill', `url(#${category}Gradient)`); | |
| area.setAttribute('opacity', '0.3'); | |
| area.setAttribute('id', `area-${category}`); | |
| chartLines.insertBefore(area, line); | |
| }); | |
| } | |
| function updateChart() { | |
| if (currentIndex >= processedData.length) return; | |
| const currentData = processedData.slice(0, currentIndex + 1); | |
| Object.keys(colors).forEach(category => { | |
| const line = document.getElementById(`line-${category}`); | |
| const area = document.getElementById(`area-${category}`); | |
| const points = currentData.map(d => `${timeToX(d.seconds)},${countToY(d.wordCounts[category])}`).join(' '); | |
| line.setAttribute('points', points); | |
| // Create area points | |
| if (currentData.length > 0) { | |
| const areaPoints = []; | |
| areaPoints.push(`${timeToX(currentData[0].seconds)},${chartTop + chartHeight}`); | |
| currentData.forEach(d => { | |
| areaPoints.push(`${timeToX(d.seconds)},${countToY(d.wordCounts[category])}`); | |
| }); | |
| areaPoints.push(`${timeToX(currentData[currentData.length - 1].seconds)},${chartTop + chartHeight}`); | |
| area.setAttribute('points', areaPoints.join(' ')); | |
| } | |
| }); | |
| // Update time indicator | |
| const timeIndicator = document.getElementById('time-indicator'); | |
| const currentX = timeToX(processedData[currentIndex].seconds); | |
| timeIndicator.setAttribute('x1', currentX); | |
| timeIndicator.setAttribute('x2', currentX); | |
| } | |
| function updateTeleprompter() { | |
| if (currentIndex >= processedData.length) return; | |
| const current = processedData[currentIndex]; | |
| document.getElementById('timestamp').textContent = current.time; | |
| // Highlight keywords in text | |
| let highlightedText = current.text; | |
| // Remove emojis and highlight the words that were between them | |
| const allWords = [...current.words.vision, ...current.words.action, ...current.words.thought, ...current.words.context]; | |
| allWords.forEach(word => { | |
| const regex = new RegExp(`\\b${word}\\b`, 'gi'); | |
| highlightedText = highlightedText.replace(regex, `<span class="highlighted-word">${word}</span>`); | |
| }); | |
| // Remove emoji markers | |
| highlightedText = highlightedText.replace(/[ποΈπ£οΈπͺπ€ππππ―ππ‘π«οΈπ]/g, ''); | |
| document.getElementById('current-text').innerHTML = highlightedText; | |
| // Update progress bar | |
| const progress = ((currentIndex + 1) / processedData.length) * 100; | |
| document.getElementById('progress-fill').style.width = `${progress}%`; | |
| } | |
| function animate() { | |
| if (!isPlaying) return; | |
| updateChart(); | |
| updateTeleprompter(); | |
| currentIndex++; | |
| if (currentIndex < processedData.length) { | |
| animationTimer = setTimeout(animate, 2000 / animationSpeed); // 2 second intervals | |
| } else { | |
| document.getElementById('playButton').textContent = 'π Restart'; | |
| document.getElementById('playButton').onclick = restart; | |
| } | |
| } | |
| function togglePlayPause() { | |
| isPlaying = !isPlaying; | |
| const button = document.getElementById('playButton'); | |
| if (isPlaying) { | |
| button.textContent = 'βΈοΈ Pause'; | |
| if (currentIndex < processedData.length) { | |
| animate(); | |
| } | |
| } else { | |
| button.textContent = 'βΆοΈ Play'; | |
| clearTimeout(animationTimer); | |
| } | |
| } | |
| function restart() { | |
| currentIndex = 0; | |
| isPlaying = true; | |
| clearTimeout(animationTimer); | |
| document.getElementById('playButton').textContent = 'βΈοΈ Pause'; | |
| document.getElementById('playButton').onclick = togglePlayPause; | |
| animate(); | |
| } | |
| function changeSpeed(delta) { | |
| animationSpeed = Math.max(0.5, Math.min(3.0, animationSpeed + delta)); | |
| } | |
| // Initialize | |
| createTimeSeries(); | |
| animate(); | |
| </script> | |
| </body> | |
| </html> |