awacke1 commited on
Commit
8358f59
·
verified ·
1 Parent(s): 32960ac

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +525 -195
index.html CHANGED
@@ -1,7 +1,7 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>SVG Transcript Time Series with Teleprompter</title>
5
  <style>
6
  body {
7
  margin: 0;
@@ -13,7 +13,7 @@
13
  }
14
 
15
  .container {
16
- max-width: 1200px;
17
  margin: 0 auto;
18
  }
19
 
@@ -65,6 +65,8 @@
65
  border-radius: 15px;
66
  padding: 30px;
67
  box-shadow: 0 20px 40px rgba(0,0,0,0.1);
 
 
68
  }
69
 
70
  .cluster-legend {
@@ -83,15 +85,18 @@
83
  color: #333;
84
  }
85
 
86
- .legend-color {
87
- width: 20px;
88
- height: 20px;
89
- border-radius: 50%;
90
  }
91
 
92
  .controls {
93
  text-align: center;
94
  margin: 20px 0;
 
 
 
 
95
  }
96
 
97
  button {
@@ -99,11 +104,13 @@
99
  border: none;
100
  color: white;
101
  padding: 12px 24px;
102
- margin: 0 10px;
103
  border-radius: 25px;
104
  cursor: pointer;
105
  font-size: 16px;
106
  transition: transform 0.2s;
 
 
 
107
  }
108
 
109
  button:hover {
@@ -116,6 +123,10 @@
116
  transform: none;
117
  }
118
 
 
 
 
 
119
  .progress-bar {
120
  width: 100%;
121
  height: 6px;
@@ -132,6 +143,42 @@
132
  transition: width 0.3s ease;
133
  border-radius: 3px;
134
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  </style>
136
  </head>
137
  <body>
@@ -140,7 +187,7 @@
140
  <div class="timestamp" id="timestamp">5:48</div>
141
  <div class="teleprompter-text">
142
  <div class="current-text" id="current-text">
143
- It's a very good way of understanding how we communicate <span class="highlighted-word">linguistically</span>.
144
  </div>
145
  </div>
146
  <div class="progress-bar">
@@ -149,167 +196,225 @@
149
  </div>
150
 
151
  <div class="controls">
152
- <button onclick="togglePlayPause()" id="playButton">⏸️ Pause</button>
153
- <button onclick="restart()">🔄 Restart</button>
154
- <button onclick="changeSpeed(-0.5)">🐌 Slower</button>
155
- <button onclick="changeSpeed(0.5)">🐰 Faster</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  </div>
157
 
158
  <div class="chart-container">
159
  <div class="cluster-legend">
160
  <div class="legend-item">
161
- <div class="legend-color" style="background: #ff4757;"></div>
162
- <span>👁️ Vision/Perception</span>
 
 
163
  </div>
164
  <div class="legend-item">
165
- <div class="legend-color" style="background: #2ed573;"></div>
166
- <span>💪 Action</span>
 
 
167
  </div>
168
  <div class="legend-item">
169
- <div class="legend-color" style="background: #3742fa;"></div>
170
- <span>🤔 Thought</span>
 
 
171
  </div>
172
  <div class="legend-item">
173
- <div class="legend-color" style="background: #ffa502;"></div>
174
- <span>🌍 Context</span>
 
 
175
  </div>
176
  </div>
177
 
178
- <svg id="chart" width="100%" height="400" viewBox="0 0 800 400">
179
- <!-- Background grid -->
180
  <defs>
181
- <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
182
- <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#e0e0e0" stroke-width="1"/>
183
  </pattern>
184
 
185
- <!-- Gradients for lines -->
186
- <linearGradient id="visionGradient" x1="0%" y1="0%" x2="100%" y2="0%">
187
- <stop offset="0%" style="stop-color:#ff4757;stop-opacity:0.3" />
188
- <stop offset="100%" style="stop-color:#ff4757;stop-opacity:1" />
 
189
  </linearGradient>
190
- <linearGradient id="actionGradient" x1="0%" y1="0%" x2="100%" y2="0%">
191
- <stop offset="0%" style="stop-color:#2ed573;stop-opacity:0.3" />
192
- <stop offset="100%" style="stop-color:#2ed573;stop-opacity:1" />
 
193
  </linearGradient>
194
- <linearGradient id="thoughtGradient" x1="0%" y1="0%" x2="100%" y2="0%">
195
- <stop offset="0%" style="stop-color:#3742fa;stop-opacity:0.3" />
196
- <stop offset="100%" style="stop-color:#3742fa;stop-opacity:1" />
 
197
  </linearGradient>
198
- <linearGradient id="contextGradient" x1="0%" y1="0%" x2="100%" y2="0%">
199
- <stop offset="0%" style="stop-color:#ffa502;stop-opacity:0.3" />
200
- <stop offset="100%" style="stop-color:#ffa502;stop-opacity:1" />
 
201
  </linearGradient>
 
 
 
 
 
 
 
 
 
 
 
 
202
  </defs>
203
 
204
- <rect width="800" height="400" fill="url(#grid)" opacity="0.5"/>
205
-
206
- <!-- Axes -->
207
- <line x1="80" y1="50" x2="80" y2="350" stroke="#333" stroke-width="2"/>
208
- <line x1="80" y1="350" x2="720" y2="350" stroke="#333" stroke-width="2"/>
209
-
210
- <!-- Y-axis labels -->
211
- <text x="70" y="350" text-anchor="end" font-size="12" fill="#666">0</text>
212
- <text x="70" y="290" text-anchor="end" font-size="12" fill="#666">1</text>
213
- <text x="70" y="230" text-anchor="end" font-size="12" fill="#666">2</text>
214
- <text x="70" y="170" text-anchor="end" font-size="12" fill="#666">3</text>
215
- <text x="70" y="110" text-anchor="end" font-size="12" fill="#666">4</text>
216
- <text x="70" y="50" text-anchor="end" font-size="12" fill="#666">5</text>
217
-
218
- <!-- Axis labels -->
219
- <text x="400" y="390" text-anchor="middle" font-size="14" font-weight="bold" fill="#333">Time (MM:SS)</text>
220
- <text x="30" y="200" text-anchor="middle" font-size="14" font-weight="bold" fill="#333" transform="rotate(-90 30 200)">Word Frequency</text>
221
 
222
- <!-- Time markers will be added by JavaScript -->
223
- <g id="time-markers"></g>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
- <!-- Chart lines -->
226
- <g id="chart-lines"></g>
227
 
228
- <!-- Peak labels -->
229
- <g id="peak-labels"></g>
 
 
 
 
 
 
 
 
 
230
 
231
  <!-- Current time indicator -->
232
- <line id="time-indicator" x1="80" y1="50" x2="80" y2="350" stroke="#ff6b6b" stroke-width="3" opacity="0.8">
233
- <animate attributeName="opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/>
234
- </line>
 
 
 
 
 
235
  </svg>
236
  </div>
237
  </div>
238
 
239
  <script>
240
- // Transcript data with enhanced parsing
241
  const transcriptData = [
242
- { time: "5:48", seconds: 348, text: "It's a very good way of understanding how we communicate 🗣️linguistically🗣️." },
243
- { 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💪." },
244
- { 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🤔," },
245
- { time: "6:10", seconds: 370, text: "and it's an erroneous 🤔presumption🤔, that 👁️perception👁️ is a value free enterprise." },
246
- { 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" },
247
- { 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." },
248
- { 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" },
249
- { 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." },
250
- { 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" },
251
- { 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🌟" },
252
- { time: "6:56", seconds: 416, text: "a small element of the space that's in front of you. And the element that you 🌟choose🌟" },
253
- { 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." },
254
- { 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" },
255
- { 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" },
256
- { time: "7:25", seconds: 445, text: "to read a particularly deep 🤔thinker🤔 because you could also 🤔think🤔 of your 👁️perceptions👁️" },
257
- { 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?" },
258
- { 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." },
259
- { 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" },
260
- { 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💡?" }
261
  ];
262
 
263
- // Enhanced word extraction from emoji-marked text
264
- function extractWords(text) {
265
- const emojiPatterns = {
266
- vision: /👁️([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)👁️/g,
267
- action: /💪([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)💪/g,
268
- thought: /🤔([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🤔/g,
269
- context: /(?:🗣️([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🗣️|🌊([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🌊|🌈([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🌈|🌟([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🌟|🎯([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🎯|🌍([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🌍|💡([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)💡|🌫️([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)🌫️|👀([^👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]+)👀)/g
270
- };
271
 
272
- const words = { vision: [], action: [], thought: [], context: [] };
273
 
274
- Object.keys(emojiPatterns).forEach(category => {
275
- let match;
276
- while ((match = emojiPatterns[category].exec(text)) !== null) {
277
- const word = match.slice(1).filter(Boolean)[0];
278
- if (word) {
279
- words[category].push(word.trim());
280
- }
281
- }
282
- });
283
-
284
- return words;
285
  }
286
 
287
  // Process transcript data
288
  const processedData = transcriptData.map(entry => {
289
- const words = extractWords(entry.text);
290
- return {
291
- ...entry,
292
- words,
293
- wordCounts: {
294
- vision: words.vision.length,
295
- action: words.action.length,
296
- thought: words.thought.length,
297
- context: words.context.length
298
- }
299
- };
300
  });
301
 
302
- // Create time series data
303
- const timePoints = processedData.map(d => d.seconds);
304
- const minTime = Math.min(...timePoints);
305
- const maxTime = Math.max(...timePoints);
306
-
307
  // Chart configuration
308
- const chartWidth = 640;
309
- const chartHeight = 300;
310
- const chartLeft = 80;
311
- const chartTop = 50;
312
-
313
  const colors = {
314
  vision: '#ff4757',
315
  action: '#2ed573',
@@ -322,40 +427,92 @@ let currentIndex = 0;
322
  let isPlaying = true;
323
  let animationSpeed = 1.0;
324
  let animationTimer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
  function timeToX(seconds) {
327
- return chartLeft + ((seconds - minTime) / (maxTime - minTime)) * chartWidth;
 
 
328
  }
329
 
330
  function countToY(count) {
331
- return chartTop + chartHeight - (count * 60); // Scale factor of 60 pixels per count
 
 
 
 
332
  }
333
 
334
- function createTimeSeries() {
335
- const svg = document.getElementById('chart');
336
- const timeMarkers = document.getElementById('time-markers');
337
- const chartLines = document.getElementById('chart-lines');
338
- const peakLabels = document.getElementById('peak-labels');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
- // Add time markers
341
  processedData.forEach((entry, index) => {
342
- if (index % 3 === 0) { // Show every 3rd timestamp to avoid crowding
343
  const x = timeToX(entry.seconds);
 
344
 
345
- // Tick mark
346
  const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');
347
- tick.setAttribute('x1', x);
348
- tick.setAttribute('y1', chartTop + chartHeight);
349
- tick.setAttribute('x2', x);
350
- tick.setAttribute('y2', chartTop + chartHeight + 5);
351
  tick.setAttribute('stroke', '#666');
352
- tick.setAttribute('stroke-width', '1');
353
  timeMarkers.appendChild(tick);
354
 
 
 
 
 
 
 
 
 
 
 
 
355
  // Label
356
  const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
357
- label.setAttribute('x', x);
358
- label.setAttribute('y', chartTop + chartHeight + 20);
359
  label.setAttribute('text-anchor', 'middle');
360
  label.setAttribute('font-size', '10');
361
  label.setAttribute('fill', '#666');
@@ -363,57 +520,124 @@ function createTimeSeries() {
363
  timeMarkers.appendChild(label);
364
  }
365
  });
 
 
 
 
 
 
 
 
 
366
 
367
- // Create lines for each category
368
- Object.keys(colors).forEach(category => {
369
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
370
- line.setAttribute('fill', 'none');
371
- line.setAttribute('stroke', colors[category]);
372
- line.setAttribute('stroke-width', '3');
373
- line.setAttribute('stroke-linecap', 'round');
374
- line.setAttribute('stroke-linejoin', 'round');
375
- line.setAttribute('id', `line-${category}`);
376
- line.style.filter = 'drop-shadow(2px 2px 4px rgba(0,0,0,0.3))';
377
- chartLines.appendChild(line);
378
 
379
- // Area under curve
380
- const area = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
381
- area.setAttribute('fill', `url(#${category}Gradient)`);
382
- area.setAttribute('opacity', '0.3');
383
- area.setAttribute('id', `area-${category}`);
384
- chartLines.insertBefore(area, line);
385
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  }
387
 
388
- function updateChart() {
389
- if (currentIndex >= processedData.length) return;
 
 
 
390
 
391
  const currentData = processedData.slice(0, currentIndex + 1);
392
 
393
- Object.keys(colors).forEach(category => {
394
- const line = document.getElementById(`line-${category}`);
395
- const area = document.getElementById(`area-${category}`);
396
-
397
- const points = currentData.map(d => `${timeToX(d.seconds)},${countToY(d.wordCounts[category])}`).join(' ');
398
- line.setAttribute('points', points);
399
-
400
- // Create area points
401
- if (currentData.length > 0) {
402
- const areaPoints = [];
403
- areaPoints.push(`${timeToX(currentData[0].seconds)},${chartTop + chartHeight}`);
404
  currentData.forEach(d => {
405
- areaPoints.push(`${timeToX(d.seconds)},${countToY(d.wordCounts[category])}`);
 
 
 
 
 
 
 
 
 
 
 
 
406
  });
407
- areaPoints.push(`${timeToX(currentData[currentData.length - 1].seconds)},${chartTop + chartHeight}`);
408
- area.setAttribute('points', areaPoints.join(' '));
 
 
 
 
 
 
 
 
 
 
 
409
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  });
411
 
 
 
 
412
  // Update time indicator
413
- const timeIndicator = document.getElementById('time-indicator');
414
  const currentX = timeToX(processedData[currentIndex].seconds);
415
- timeIndicator.setAttribute('x1', currentX);
416
- timeIndicator.setAttribute('x2', currentX);
 
 
417
  }
418
 
419
  function updateTeleprompter() {
@@ -422,40 +646,79 @@ function updateTeleprompter() {
422
  const current = processedData[currentIndex];
423
  document.getElementById('timestamp').textContent = current.time;
424
 
425
- // Highlight keywords in text
426
  let highlightedText = current.text;
427
 
428
- // Remove emojis and highlight the words that were between them
429
- const allWords = [...current.words.vision, ...current.words.action, ...current.words.thought, ...current.words.context];
 
 
 
 
430
 
431
- allWords.forEach(word => {
432
- const regex = new RegExp(`\\b${word}\\b`, 'gi');
433
- highlightedText = highlightedText.replace(regex, `<span class="highlighted-word">${word}</span>`);
 
 
434
  });
435
 
436
- // Remove emoji markers
437
- highlightedText = highlightedText.replace(/[👁️🗣️💪🤔🌊🌈🌟🎯🌍💡🌫️👀]/g, '');
438
 
439
- document.getElementById('current-text').innerHTML = highlightedText;
 
 
 
 
 
 
440
 
441
  // Update progress bar
442
  const progress = ((currentIndex + 1) / processedData.length) * 100;
443
  document.getElementById('progress-fill').style.width = `${progress}%`;
444
  }
445
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  function animate() {
447
  if (!isPlaying) return;
448
 
449
- updateChart();
450
  updateTeleprompter();
451
 
452
  currentIndex++;
453
 
454
  if (currentIndex < processedData.length) {
455
- animationTimer = setTimeout(animate, 2000 / animationSpeed); // 2 second intervals
456
  } else {
457
- document.getElementById('playButton').textContent = '🔄 Restart';
458
- document.getElementById('playButton').onclick = restart;
 
 
 
 
 
 
 
 
 
 
459
  }
460
  }
461
 
@@ -464,13 +727,24 @@ function togglePlayPause() {
464
  const button = document.getElementById('playButton');
465
 
466
  if (isPlaying) {
467
- button.textContent = '⏸️ Pause';
 
 
 
 
 
468
  if (currentIndex < processedData.length) {
469
  animate();
470
  }
471
  } else {
472
- button.textContent = '▶️ Play';
 
 
 
 
 
473
  clearTimeout(animationTimer);
 
474
  }
475
  }
476
 
@@ -478,7 +752,14 @@ function restart() {
478
  currentIndex = 0;
479
  isPlaying = true;
480
  clearTimeout(animationTimer);
481
- document.getElementById('playButton').textContent = '⏸️ Pause';
 
 
 
 
 
 
 
482
  document.getElementById('playButton').onclick = togglePlayPause;
483
  animate();
484
  }
@@ -487,8 +768,57 @@ function changeSpeed(delta) {
487
  animationSpeed = Math.max(0.5, Math.min(3.0, animationSpeed + delta));
488
  }
489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  // Initialize
491
- createTimeSeries();
 
492
  animate();
493
 
494
  </script>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>3D SVG Transcript Time Series with Teleprompter</title>
5
  <style>
6
  body {
7
  margin: 0;
 
13
  }
14
 
15
  .container {
16
+ max-width: 1400px;
17
  margin: 0 auto;
18
  }
19
 
 
65
  border-radius: 15px;
66
  padding: 30px;
67
  box-shadow: 0 20px 40px rgba(0,0,0,0.1);
68
+ position: relative;
69
+ overflow: hidden;
70
  }
71
 
72
  .cluster-legend {
 
85
  color: #333;
86
  }
87
 
88
+ .legend-icon {
89
+ width: 24px;
90
+ height: 24px;
 
91
  }
92
 
93
  .controls {
94
  text-align: center;
95
  margin: 20px 0;
96
+ display: flex;
97
+ justify-content: center;
98
+ gap: 10px;
99
+ flex-wrap: wrap;
100
  }
101
 
102
  button {
 
104
  border: none;
105
  color: white;
106
  padding: 12px 24px;
 
107
  border-radius: 25px;
108
  cursor: pointer;
109
  font-size: 16px;
110
  transition: transform 0.2s;
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 8px;
114
  }
115
 
116
  button:hover {
 
123
  transform: none;
124
  }
125
 
126
+ button.active {
127
+ background: linear-gradient(45deg, #ff6b6b, #ffa500);
128
+ }
129
+
130
  .progress-bar {
131
  width: 100%;
132
  height: 6px;
 
143
  transition: width 0.3s ease;
144
  border-radius: 3px;
145
  }
146
+
147
+ .chart-3d {
148
+ perspective: 1000px;
149
+ transform-style: preserve-3d;
150
+ }
151
+
152
+ .surface-layer {
153
+ position: absolute;
154
+ transform-style: preserve-3d;
155
+ transition: all 0.5s ease;
156
+ }
157
+
158
+ .surface-mesh {
159
+ opacity: 0.7;
160
+ stroke-width: 1;
161
+ fill-opacity: 0.3;
162
+ }
163
+
164
+ .interconnection-surface {
165
+ opacity: 0.4;
166
+ transition: opacity 0.5s ease;
167
+ }
168
+
169
+ .axis-3d {
170
+ stroke-width: 2;
171
+ stroke: #333;
172
+ }
173
+
174
+ .speaking {
175
+ animation: speaking 0.5s ease-in-out infinite alternate;
176
+ }
177
+
178
+ @keyframes speaking {
179
+ from { color: #ff6b6b; }
180
+ to { color: #ffa500; }
181
+ }
182
  </style>
183
  </head>
184
  <body>
 
187
  <div class="timestamp" id="timestamp">5:48</div>
188
  <div class="teleprompter-text">
189
  <div class="current-text" id="current-text">
190
+ It's a very good way of understanding how we communicate linguistically.
191
  </div>
192
  </div>
193
  <div class="progress-bar">
 
196
  </div>
197
 
198
  <div class="controls">
199
+ <button onclick="togglePlayPause()" id="playButton">
200
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
201
+ <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
202
+ </svg>
203
+ Pause
204
+ </button>
205
+ <button onclick="restart()">
206
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
207
+ <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
208
+ </svg>
209
+ Restart
210
+ </button>
211
+ <button onclick="changeSpeed(-0.5)">
212
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
213
+ <path d="M13.5 13c-1.5 1.5-1.5 1.5-3 0s-1.5-1.5 0-3 1.5-1.5 3 0 1.5 1.5 0 3zm4.5-5H8l4-4h6z"/>
214
+ </svg>
215
+ Slower
216
+ </button>
217
+ <button onclick="changeSpeed(0.5)">
218
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
219
+ <path d="M10.5 11c1.5-1.5 1.5-1.5 3 0s1.5 1.5 0 3-1.5 1.5-3 0-1.5-1.5 0-3zM6 6h8l-4 4H6z"/>
220
+ </svg>
221
+ Faster
222
+ </button>
223
+ <button onclick="toggleAutoRepeat()" id="autoRepeatButton">
224
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
225
+ <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
226
+ <circle cx="12" cy="12" r="2"/>
227
+ </svg>
228
+ Auto-Repeat
229
+ </button>
230
+ <button onclick="toggleReadAloud()" id="readAloudButton">
231
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
232
+ <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
233
+ </svg>
234
+ Read Aloud
235
+ </button>
236
  </div>
237
 
238
  <div class="chart-container">
239
  <div class="cluster-legend">
240
  <div class="legend-item">
241
+ <svg class="legend-icon" viewBox="0 0 24 24" fill="#ff4757">
242
+ <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
243
+ </svg>
244
+ <span>Vision/Perception</span>
245
  </div>
246
  <div class="legend-item">
247
+ <svg class="legend-icon" viewBox="0 0 24 24" fill="#2ed573">
248
+ <path d="M19 12.998h-6v6h-2v-6H5v-2h6v-6h2v6h6z"/>
249
+ </svg>
250
+ <span>Action</span>
251
  </div>
252
  <div class="legend-item">
253
+ <svg class="legend-icon" viewBox="0 0 24 24" fill="#3742fa">
254
+ <path d="M21 4V2h-2v2h-2.5l-.5 2h1.5v1.5c0 .83-.67 1.5-1.5 1.5S15 8.33 15 7.5V6h1.5l-.5-2H13V2h-2v2H9.5l-.5 2H10.5v1.5c0 .83-.67 1.5-1.5 1.5S8 8.33 8 7.5V6h1.5L9 4H6V2H4v2H2v2h2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6h2V4h-1zm-2 14H5V6h14v12z"/>
255
+ </svg>
256
+ <span>Thought</span>
257
  </div>
258
  <div class="legend-item">
259
+ <svg class="legend-icon" viewBox="0 0 24 24" fill="#ffa502">
260
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
261
+ </svg>
262
+ <span>Context</span>
263
  </div>
264
  </div>
265
 
266
+ <svg id="chart" width="100%" height="500" viewBox="0 0 1000 500" class="chart-3d">
267
+ <!-- 3D Grid Background -->
268
  <defs>
269
+ <pattern id="grid3d" width="50" height="50" patternUnits="userSpaceOnUse">
270
+ <path d="M 50 0 L 0 0 0 50" fill="none" stroke="#e0e0e0" stroke-width="1" opacity="0.5"/>
271
  </pattern>
272
 
273
+ <!-- Gradients for 3D surfaces -->
274
+ <linearGradient id="visionSurface" x1="0%" y1="0%" x2="100%" y2="100%">
275
+ <stop offset="0%" style="stop-color:#ff4757;stop-opacity:0.8" />
276
+ <stop offset="50%" style="stop-color:#ff6b6b;stop-opacity:0.6" />
277
+ <stop offset="100%" style="stop-color:#ff4757;stop-opacity:0.4" />
278
  </linearGradient>
279
+ <linearGradient id="actionSurface" x1="0%" y1="0%" x2="100%" y2="100%">
280
+ <stop offset="0%" style="stop-color:#2ed573;stop-opacity:0.8" />
281
+ <stop offset="50%" style="stop-color:#55efc4;stop-opacity:0.6" />
282
+ <stop offset="100%" style="stop-color:#2ed573;stop-opacity:0.4" />
283
  </linearGradient>
284
+ <linearGradient id="thoughtSurface" x1="0%" y1="0%" x2="100%" y2="100%">
285
+ <stop offset="0%" style="stop-color:#3742fa;stop-opacity:0.8" />
286
+ <stop offset="50%" style="stop-color:#5352ed;stop-opacity:0.6" />
287
+ <stop offset="100%" style="stop-color:#3742fa;stop-opacity:0.4" />
288
  </linearGradient>
289
+ <linearGradient id="contextSurface" x1="0%" y1="0%" x2="100%" y2="100%">
290
+ <stop offset="0%" style="stop-color:#ffa502;stop-opacity:0.8" />
291
+ <stop offset="50%" style="stop-color:#ffb142;stop-opacity:0.6" />
292
+ <stop offset="100%" style="stop-color:#ffa502;stop-opacity:0.4" />
293
  </linearGradient>
294
+
295
+ <!-- Interconnection gradients -->
296
+ <radialGradient id="interconnection1">
297
+ <stop offset="0%" style="stop-color:#ff4757;stop-opacity:0.3" />
298
+ <stop offset="50%" style="stop-color:#2ed573;stop-opacity:0.2" />
299
+ <stop offset="100%" style="stop-color:#3742fa;stop-opacity:0.1" />
300
+ </radialGradient>
301
+ <radialGradient id="interconnection2">
302
+ <stop offset="0%" style="stop-color:#2ed573;stop-opacity:0.3" />
303
+ <stop offset="50%" style="stop-color:#ffa502;stop-opacity:0.2" />
304
+ <stop offset="100%" style="stop-color:#ff4757;stop-opacity:0.1" />
305
+ </radialGradient>
306
  </defs>
307
 
308
+ <!-- 3D Background Grid -->
309
+ <g id="grid-3d" transform="translate(100, 50)">
310
+ <!-- Base grid -->
311
+ <rect width="800" height="400" fill="url(#grid3d)" opacity="0.3"/>
312
+
313
+ <!-- 3D depth lines -->
314
+ <g id="depth-lines">
315
+ <!-- Will be populated by JavaScript -->
316
+ </g>
317
+ </g>
 
 
 
 
 
 
 
318
 
319
+ <!-- 3D Axes -->
320
+ <g id="axes-3d" transform="translate(100, 50)">
321
+ <!-- X-axis (Time) -->
322
+ <line x1="0" y1="400" x2="800" y2="400" class="axis-3d"/>
323
+ <line x1="800" y1="400" x2="750" y2="350" class="axis-3d" opacity="0.7"/>
324
+
325
+ <!-- Y-axis (Frequency) -->
326
+ <line x1="0" y1="400" x2="0" y2="0" class="axis-3d"/>
327
+ <line x1="0" y1="0" x2="50" y2="50" class="axis-3d" opacity="0.7"/>
328
+
329
+ <!-- Z-axis (Categories) -->
330
+ <line x1="0" y1="400" x2="200" y2="250" class="axis-3d"/>
331
+
332
+ <!-- Axis labels -->
333
+ <text x="400" y="440" text-anchor="middle" font-size="14" font-weight="bold" fill="#333">Time (MM:SS)</text>
334
+ <text x="-20" y="200" text-anchor="middle" font-size="14" font-weight="bold" fill="#333" transform="rotate(-90 -20 200)">Frequency</text>
335
+ <text x="100" y="320" text-anchor="middle" font-size="14" font-weight="bold" fill="#333" transform="rotate(-45 100 320)">Semantic Clusters</text>
336
+ </g>
337
 
338
+ <!-- Time markers -->
339
+ <g id="time-markers-3d"></g>
340
 
341
+ <!-- 3D Surfaces -->
342
+ <g id="surfaces-3d" transform="translate(100, 50)">
343
+ <!-- Individual category surfaces -->
344
+ <g id="vision-surface"></g>
345
+ <g id="action-surface"></g>
346
+ <g id="thought-surface"></g>
347
+ <g id="context-surface"></g>
348
+
349
+ <!-- Interconnection surfaces -->
350
+ <g id="interconnections"></g>
351
+ </g>
352
 
353
  <!-- Current time indicator -->
354
+ <g id="time-indicator-3d" transform="translate(100, 50)">
355
+ <line x1="0" y1="0" x2="0" y2="400" stroke="#ff6b6b" stroke-width="4" opacity="0.8">
356
+ <animate attributeName="opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/>
357
+ </line>
358
+ <line x1="0" y1="400" x2="50" y2="350" stroke="#ff6b6b" stroke-width="3" opacity="0.6">
359
+ <animate attributeName="opacity" values="0.3;0.8;0.3" dur="2s" repeatCount="indefinite"/>
360
+ </line>
361
+ </g>
362
  </svg>
363
  </div>
364
  </div>
365
 
366
  <script>
367
+ // Enhanced transcript data with better emoji parsing
368
  const transcriptData = [
369
+ { time: "5:48", seconds: 348, text: "It's a very good way of understanding how we communicate linguistically." },
370
+ { 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." },
371
+ { 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," },
372
+ { time: "6:10", seconds: 370, text: "and it's an erroneous presumption, that perception is a value free enterprise." },
373
+ { 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" },
374
+ { 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." },
375
+ { 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" },
376
+ { 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." },
377
+ { 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" },
378
+ { 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" },
379
+ { time: "6:56", seconds: 416, text: "a small element of the space that's in front of you. And the element that you choose" },
380
+ { 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." },
381
+ { 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" },
382
+ { 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" },
383
+ { time: "7:25", seconds: 445, text: "to read a particularly deep thinker because you could also think of your perceptions" },
384
+ { 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?" },
385
+ { 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." },
386
+ { 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" },
387
+ { 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?" }
388
  ];
389
 
390
+ // Enhanced semantic analysis
391
+ function analyzeSemantics(text) {
392
+ const visionWords = ['visual', 'perception', 'perceptions', 'perceive', 'look', 'looking', 'see', 'eyes', 'reveals'];
393
+ const actionWords = ['action', 'actions', 'transform', 'sampling', 'sample', 'choose', 'moving', 'active', 'predicated'];
394
+ const thoughtWords = ['think', 'thinker', 'thinkers', 'thought', 'thinking', 'presumption', 'assume', 'idea', 'axioms', 'conceptualize'];
395
+ const contextWords = ['linguistically', 'communicating', 'world', 'complexity', 'aims', 'goals', 'strategy', 'enterprise', 'experience'];
 
 
396
 
397
+ const words = text.toLowerCase().match(/\b\w+\b/g) || [];
398
 
399
+ return {
400
+ vision: words.filter(w => visionWords.includes(w)).length,
401
+ action: words.filter(w => actionWords.includes(w)).length,
402
+ thought: words.filter(w => thoughtWords.includes(w)).length,
403
+ context: words.filter(w => contextWords.includes(w)).length
404
+ };
 
 
 
 
 
405
  }
406
 
407
  // Process transcript data
408
  const processedData = transcriptData.map(entry => {
409
+ const wordCounts = analyzeSemantics(entry.text);
410
+ return { ...entry, wordCounts };
 
 
 
 
 
 
 
 
 
411
  });
412
 
 
 
 
 
 
413
  // Chart configuration
414
+ const chartWidth = 800;
415
+ const chartHeight = 400;
416
+ const depthOffset = 200;
417
+ const categories = ['vision', 'action', 'thought', 'context'];
 
418
  const colors = {
419
  vision: '#ff4757',
420
  action: '#2ed573',
 
427
  let isPlaying = true;
428
  let animationSpeed = 1.0;
429
  let animationTimer;
430
+ let autoRepeat = false;
431
+ let readAloud = false;
432
+ let speechSynthesis = window.speechSynthesis;
433
+ let currentUtterance = null;
434
+
435
+ // 3D projection helpers
436
+ function project3D(x, y, z, depth = depthOffset) {
437
+ const perspective = 1000;
438
+ const scale = perspective / (perspective + z);
439
+ return {
440
+ x: x + (z * 0.3),
441
+ y: y - (z * 0.15),
442
+ scale: scale
443
+ };
444
+ }
445
 
446
  function timeToX(seconds) {
447
+ const minTime = Math.min(...processedData.map(d => d.seconds));
448
+ const maxTime = Math.max(...processedData.map(d => d.seconds));
449
+ return (seconds - minTime) / (maxTime - minTime) * chartWidth;
450
  }
451
 
452
  function countToY(count) {
453
+ return chartHeight - (count * 80); // Scale factor
454
+ }
455
+
456
+ function categoryToZ(categoryIndex) {
457
+ return categoryIndex * 50;
458
  }
459
 
460
+ function create3DDepthLines() {
461
+ const depthLines = document.getElementById('depth-lines');
462
+
463
+ // Create depth perception lines
464
+ for (let i = 0; i <= 10; i++) {
465
+ for (let j = 0; j <= 8; j++) {
466
+ const x1 = i * 80;
467
+ const y1 = j * 50;
468
+ const projected = project3D(x1, y1, 100);
469
+
470
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
471
+ line.setAttribute('x1', x1);
472
+ line.setAttribute('y1', y1);
473
+ line.setAttribute('x2', projected.x);
474
+ line.setAttribute('y2', projected.y);
475
+ line.setAttribute('stroke', '#ddd');
476
+ line.setAttribute('stroke-width', '0.5');
477
+ line.setAttribute('opacity', '0.3');
478
+ depthLines.appendChild(line);
479
+ }
480
+ }
481
+ }
482
+
483
+ function createTimeMarkers3D() {
484
+ const timeMarkers = document.getElementById('time-markers-3d');
485
 
 
486
  processedData.forEach((entry, index) => {
487
+ if (index % 3 === 0) {
488
  const x = timeToX(entry.seconds);
489
+ const projected = project3D(x, chartHeight, 0);
490
 
491
+ // Main tick
492
  const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');
493
+ tick.setAttribute('x1', 100 + x);
494
+ tick.setAttribute('y1', 50 + chartHeight);
495
+ tick.setAttribute('x2', 100 + x);
496
+ tick.setAttribute('y2', 50 + chartHeight + 10);
497
  tick.setAttribute('stroke', '#666');
498
+ tick.setAttribute('stroke-width', '2');
499
  timeMarkers.appendChild(tick);
500
 
501
+ // 3D depth tick
502
+ const depthTick = document.createElementNS('http://www.w3.org/2000/svg', 'line');
503
+ depthTick.setAttribute('x1', 100 + x);
504
+ depthTick.setAttribute('y1', 50 + chartHeight);
505
+ depthTick.setAttribute('x2', 100 + projected.x);
506
+ depthTick.setAttribute('y2', 50 + projected.y);
507
+ depthTick.setAttribute('stroke', '#666');
508
+ depthTick.setAttribute('stroke-width', '1');
509
+ depthTick.setAttribute('opacity', '0.7');
510
+ timeMarkers.appendChild(depthTick);
511
+
512
  // Label
513
  const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
514
+ label.setAttribute('x', 100 + x);
515
+ label.setAttribute('y', 50 + chartHeight + 25);
516
  label.setAttribute('text-anchor', 'middle');
517
  label.setAttribute('font-size', '10');
518
  label.setAttribute('fill', '#666');
 
520
  timeMarkers.appendChild(label);
521
  }
522
  });
523
+ }
524
+
525
+ function createSurface3D(categoryIndex, points, color) {
526
+ const surfaceGroup = document.getElementById(`${categories[categoryIndex]}-surface`);
527
+ surfaceGroup.innerHTML = ''; // Clear existing
528
+
529
+ if (points.length < 2) return;
530
+
531
+ const z = categoryToZ(categoryIndex);
532
 
533
+ // Create surface mesh
534
+ for (let i = 0; i < points.length - 1; i++) {
535
+ const curr = points[i];
536
+ const next = points[i + 1];
 
 
 
 
 
 
 
537
 
538
+ // Project points to 3D
539
+ const p1 = project3D(curr.x, curr.y, z);
540
+ const p2 = project3D(next.x, next.y, z);
541
+ const p3 = project3D(next.x, chartHeight, z);
542
+ const p4 = project3D(curr.x, chartHeight, z);
543
+
544
+ // Create surface quad
545
+ const surface = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
546
+ surface.setAttribute('points', `${p1.x},${p1.y} ${p2.x},${p2.y} ${p3.x},${p3.y} ${p4.x},${p4.y}`);
547
+ surface.setAttribute('fill', `url(#${categories[categoryIndex]}Surface)`);
548
+ surface.setAttribute('stroke', color);
549
+ surface.setAttribute('stroke-width', '1');
550
+ surface.setAttribute('class', 'surface-mesh');
551
+ surfaceGroup.appendChild(surface);
552
+
553
+ // Create top edge line
554
+ const topLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
555
+ topLine.setAttribute('x1', p1.x);
556
+ topLine.setAttribute('y1', p1.y);
557
+ topLine.setAttribute('x2', p2.x);
558
+ topLine.setAttribute('y2', p2.y);
559
+ topLine.setAttribute('stroke', color);
560
+ topLine.setAttribute('stroke-width', '2');
561
+ topLine.setAttribute('opacity', '0.8');
562
+ surfaceGroup.appendChild(topLine);
563
+ }
564
  }
565
 
566
+ function createInterconnectionSurfaces() {
567
+ const interconnections = document.getElementById('interconnections');
568
+ interconnections.innerHTML = '';
569
+
570
+ if (currentIndex < 1) return;
571
 
572
  const currentData = processedData.slice(0, currentIndex + 1);
573
 
574
+ // Create interconnection surfaces between categories
575
+ for (let i = 0; i < categories.length - 1; i++) {
576
+ for (let j = i + 1; j < categories.length; j++) {
577
+ const cat1 = categories[i];
578
+ const cat2 = categories[j];
579
+ const z1 = categoryToZ(i);
580
+ const z2 = categoryToZ(j);
581
+
582
+ // Create interpolated surface between categories
583
+ const points = [];
 
584
  currentData.forEach(d => {
585
+ const x = timeToX(d.seconds);
586
+ const y1 = countToY(d.wordCounts[cat1]);
587
+ const y2 = countToY(d.wordCounts[cat2]);
588
+
589
+ // Create bridge between the two surfaces
590
+ const midZ = (z1 + z2) / 2;
591
+ const midY = (y1 + y2) / 2;
592
+
593
+ const p1 = project3D(x, y1, z1);
594
+ const p2 = project3D(x, y2, z2);
595
+ const pMid = project3D(x, midY, midZ);
596
+
597
+ points.push({p1, p2, pMid, x});
598
  });
599
+
600
+ // Create connecting surface
601
+ for (let k = 0; k < points.length - 1; k++) {
602
+ const curr = points[k];
603
+ const next = points[k + 1];
604
+
605
+ const surface = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
606
+ surface.setAttribute('points',
607
+ `${curr.p1.x},${curr.p1.y} ${curr.pMid.x},${curr.pMid.y} ${next.pMid.x},${next.pMid.y} ${next.p1.x},${next.p1.y}`);
608
+ surface.setAttribute('fill', `url(#interconnection${(i + j) % 2 + 1})`);
609
+ surface.setAttribute('class', 'interconnection-surface');
610
+ interconnections.appendChild(surface);
611
+ }
612
  }
613
+ }
614
+ }
615
+
616
+ function update3DChart() {
617
+ if (currentIndex >= processedData.length) return;
618
+
619
+ const currentData = processedData.slice(0, currentIndex + 1);
620
+
621
+ // Update each category surface
622
+ categories.forEach((category, index) => {
623
+ const points = currentData.map(d => ({
624
+ x: timeToX(d.seconds),
625
+ y: countToY(d.wordCounts[category])
626
+ }));
627
+
628
+ createSurface3D(index, points, colors[category]);
629
  });
630
 
631
+ // Update interconnection surfaces
632
+ createInterconnectionSurfaces();
633
+
634
  // Update time indicator
635
+ const timeIndicator = document.getElementById('time-indicator-3d');
636
  const currentX = timeToX(processedData[currentIndex].seconds);
637
+ timeIndicator.querySelector('line:first-child').setAttribute('x1', currentX);
638
+ timeIndicator.querySelector('line:first-child').setAttribute('x2', currentX);
639
+ timeIndicator.querySelector('line:last-child').setAttribute('x1', currentX);
640
+ timeIndicator.querySelector('line:last-child').setAttribute('x2', currentX + 50);
641
  }
642
 
643
  function updateTeleprompter() {
 
646
  const current = processedData[currentIndex];
647
  document.getElementById('timestamp').textContent = current.time;
648
 
649
+ // Highlight key words based on word counts
650
  let highlightedText = current.text;
651
 
652
+ // Find words to highlight based on semantic analysis
653
+ const words = current.text.toLowerCase().match(/\b\w+\b/g) || [];
654
+ const visionWords = ['visual', 'perception', 'perceptions', 'perceive', 'look', 'looking', 'see', 'eyes', 'reveals'];
655
+ const actionWords = ['action', 'actions', 'transform', 'sampling', 'sample', 'choose', 'moving', 'active', 'predicated'];
656
+ const thoughtWords = ['think', 'thinker', 'thinkers', 'thought', 'thinking', 'presumption', 'assume', 'idea', 'axioms', 'conceptualize'];
657
+ const contextWords = ['linguistically', 'communicating', 'world', 'complexity', 'aims', 'goals', 'strategy', 'enterprise', 'experience'];
658
 
659
+ [visionWords, actionWords, thoughtWords, contextWords].forEach(wordList => {
660
+ wordList.forEach(word => {
661
+ const regex = new RegExp(`\\b(${word})\\b`, 'gi');
662
+ highlightedText = highlightedText.replace(regex, '<span class="highlighted-word">$1</span>');
663
+ });
664
  });
665
 
666
+ const textElement = document.getElementById('current-text');
667
+ textElement.innerHTML = highlightedText;
668
 
669
+ // Add speaking class if read aloud is enabled
670
+ if (readAloud) {
671
+ textElement.classList.add('speaking');
672
+ speakText(current.text);
673
+ } else {
674
+ textElement.classList.remove('speaking');
675
+ }
676
 
677
  // Update progress bar
678
  const progress = ((currentIndex + 1) / processedData.length) * 100;
679
  document.getElementById('progress-fill').style.width = `${progress}%`;
680
  }
681
 
682
+ function speakText(text) {
683
+ if (!readAloud || !speechSynthesis) return;
684
+
685
+ // Stop any current speech
686
+ speechSynthesis.cancel();
687
+
688
+ // Clean text for speech
689
+ const cleanText = text.replace(/[^\w\s.,!?]/g, '');
690
+
691
+ currentUtterance = new SpeechSynthesisUtterance(cleanText);
692
+ currentUtterance.rate = 0.8;
693
+ currentUtterance.pitch = 1.0;
694
+ currentUtterance.volume = 0.8;
695
+
696
+ speechSynthesis.speak(currentUtterance);
697
+ }
698
+
699
  function animate() {
700
  if (!isPlaying) return;
701
 
702
+ update3DChart();
703
  updateTeleprompter();
704
 
705
  currentIndex++;
706
 
707
  if (currentIndex < processedData.length) {
708
+ animationTimer = setTimeout(animate, 3000 / animationSpeed);
709
  } else {
710
+ // Animation finished
711
+ if (autoRepeat) {
712
+ restart();
713
+ } else {
714
+ document.getElementById('playButton').innerHTML = `
715
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
716
+ <path d="M8 5v14l11-7z"/>
717
+ </svg>
718
+ Restart
719
+ `;
720
+ document.getElementById('playButton').onclick = restart;
721
+ }
722
  }
723
  }
724
 
 
727
  const button = document.getElementById('playButton');
728
 
729
  if (isPlaying) {
730
+ button.innerHTML = `
731
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
732
+ <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
733
+ </svg>
734
+ Pause
735
+ `;
736
  if (currentIndex < processedData.length) {
737
  animate();
738
  }
739
  } else {
740
+ button.innerHTML = `
741
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
742
+ <path d="M8 5v14l11-7z"/>
743
+ </svg>
744
+ Play
745
+ `;
746
  clearTimeout(animationTimer);
747
+ speechSynthesis.cancel();
748
  }
749
  }
750
 
 
752
  currentIndex = 0;
753
  isPlaying = true;
754
  clearTimeout(animationTimer);
755
+ speechSynthesis.cancel();
756
+
757
+ document.getElementById('playButton').innerHTML = `
758
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
759
+ <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
760
+ </svg>
761
+ Pause
762
+ `;
763
  document.getElementById('playButton').onclick = togglePlayPause;
764
  animate();
765
  }
 
768
  animationSpeed = Math.max(0.5, Math.min(3.0, animationSpeed + delta));
769
  }
770
 
771
+ function toggleAutoRepeat() {
772
+ autoRepeat = !autoRepeat;
773
+ const button = document.getElementById('autoRepeatButton');
774
+
775
+ if (autoRepeat) {
776
+ button.classList.add('active');
777
+ button.innerHTML = `
778
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
779
+ <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
780
+ <circle cx="12" cy="12" r="2"/>
781
+ </svg>
782
+ Auto-Repeat ON
783
+ `;
784
+ } else {
785
+ button.classList.remove('active');
786
+ button.innerHTML = `
787
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
788
+ <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
789
+ </svg>
790
+ Auto-Repeat
791
+ `;
792
+ }
793
+ }
794
+
795
+ function toggleReadAloud() {
796
+ readAloud = !readAloud;
797
+ const button = document.getElementById('readAloudButton');
798
+
799
+ if (readAloud) {
800
+ button.classList.add('active');
801
+ button.innerHTML = `
802
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
803
+ <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
804
+ </svg>
805
+ Read Aloud ON
806
+ `;
807
+ } else {
808
+ button.classList.remove('active');
809
+ button.innerHTML = `
810
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
811
+ <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
812
+ </svg>
813
+ Read Aloud
814
+ `;
815
+ speechSynthesis.cancel();
816
+ }
817
+ }
818
+
819
  // Initialize
820
+ create3DDepthLines();
821
+ createTimeMarkers3D();
822
  animate();
823
 
824
  </script>