Transaction

90a542bc03e33a59b67cf6922b4ea34bf93fda7b528cf8a3a476b5458fac17c3
Timestamp (utc)
2025-08-20 07:20:41
Fee Paid
0.00000001 BSV
(
0.00000001 BSV
-
0.00000000 BSV
)
Fee Rate
4.149 sat/KB
Version
1
Confirmations
20,508
Size Stats
241 B

1 Input

Total Input:
0.00000001 BSV
  • v© £üaÍïêjj&@Ú:‹ø'šÀ_ψ¬cordQ text/htmlMêš<!doctype html><html><head></head><body><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Enhanced BSV Ordinals Audio Player</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; padding: 0; min-height: 100vh; overflow: hidden; background: #000; display: flex; justify-content: center; align-items: center; } .wrapper { position: relative; width: 100vw; height: 100vh; } #particle-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; } #frequency-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: auto; z-index: 2; mix-blend-mode: screen; cursor: crosshair; } .controls { position: absolute; top: 20px; right: 20px; z-index: 3; display: flex; gap: 10px; } .control-button { background: rgba(0, 20, 40, 0.8); border: 1px solid rgba(0, 247, 255, 0.5); width: 50px; height: 50px; border-radius: 50%; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; opacity: 0.7; padding: 0; backdrop-filter: blur(10px); } .control-button:hover { opacity: 1; border-color: rgba(0, 247, 255, 0.8); transform: scale(1.1); box-shadow: 0 0 20px rgba(0, 247, 255, 0.3); } .control-button .icon::before { font-size: 24px; } .ordinals-button .icon::before { content: "<µ"; } .ordinals-button.playing .icon::before { content: "ø"; } .ordinals-button.muted .icon::before { opacity: 0.4; } .loading { border: 2px solid rgba(0, 247, 255, 0.2); border-top: 2px solid rgba(0, 247, 255, 0.8); animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .audio-info { position: absolute; bottom: 20px; left: 20px; color: rgba(0, 247, 255, 0.8); font-size: 12px; z-index: 3; backdrop-filter: blur(10px); background: rgba(0, 20, 40, 0.6); padding: 10px 15px; border-radius: 8px; border: 1px solid rgba(0, 247, 255, 0.3); } @media (max-width: 768px) { .controls { top: 15px; right: 15px; } .control-button { width: 45px; height: 45px; } .control-button .icon::before { font-size: 20px; } .audio-info { bottom: 15px; left: 15px; font-size: 11px; } } </style> </head> <body> <div class="wrapper"> <canvas id="particle-canvas"></canvas> <canvas id="frequency-canvas"></canvas> <div class="controls"> <button id="ordinalsToggle" class="control-button ordinals-button muted" aria-label="Toggle ordinals audio"> <span class="icon"></span> </button> </div> <div class="audio-info" id="audioInfo"> <div>BSV Ordinals Audio Visualizer</div> <div>Frequency: <span id="dominantFreq">-</span> Hz</div> <div>Volume: <span id="volumeLevel">-</span>%</div> </div> </div> <script> class OrdinalsAudioPlayer { constructor() { this.audio = null; this.audioContext = null; this.analyser = null; this.isLoaded = false; this.isPlaying = false; this.isLoading = false; this.ordinalsUrl = 'https://ordinals.gorillapool.io/content/1bf39556a2e958b4c449b36acd549099b65fa8067c07df13459c18efbba71fbd_0'; } async initialize() { if (this.isLoaded || this.isLoading) return; this.isLoading = true; try { this.audio = new Audio(); this.audio.crossOrigin = "anonymous"; this.audio.loop = true; this.audio.volume = 0.7; this.audio.preload = 'metadata'; await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => reject(new Error('Audio loading timeout (15s)')), 15000); const resolveOnce = () => { clearTimeout(timeoutId); resolve(); }; this.audio.addEventListener('canplaythrough', resolveOnce, { once: true }); this.audio.addEventListener('error', (e) => { clearTimeout(timeoutId); reject(new Error(`Audio loading error: ${this.audio.error?.message || 'Unknown error'}`)); }, { once: true }); this.audio.src = this.ordinalsUrl; this.audio.load(); }); this.isLoaded = true; } catch (error) { console.error('Error loading ordinals audio:', error); this.isLoaded = false; throw error; } finally { this.isLoading = false; } } setupAudioContext() { if (this.audioContext) return; this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); const source = this.audioContext.createMediaElementSource(this.audio); this.analyser = this.audioContext.createAnalyser(); this.analyser.fftSize = 1024; // Increased for better frequency resolution this.analyser.smoothingTimeConstant = 0.3; // Reduced for more responsive visuals source.connect(this.analyser); this.analyser.connect(this.audioContext.destination); } async play() { if (!this.isLoaded && !this.isLoading) await this.initialize(); if (this.audio && this.isLoaded && !this.isPlaying) { this.setupAudioContext(); if (this.audioContext.state === 'suspended') await this.audioContext.resume(); await this.audio.play(); this.isPlaying = true; } } pause() { if (this.audio && this.isPlaying) { this.audio.pause(); this.isPlaying = false; } } stop() { if (this.audio) { this.audio.pause(); this.audio.currentTime = 0; this.isPlaying = false; } } getLoadingState() { return this.isLoading; } getAnalyser() { return this.analyser; } getAudioContext() { return this.audioContext; } } class EnhancedParticle { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.reset(); this.trail = []; this.maxTrailLength = 8; } reset() { this.x = Math.random() * this.canvas.width; this.y = Math.random() * this.canvas.height; this.size = Math.random() * 3 + 1; this.baseSize = this.size; this.speedX = (Math.random() - 0.5) * 2; this.speedY = (Math.random() - 0.5) * 2; this.baseSpeedX = this.speedX; this.baseSpeedY = this.speedY; this.hue = 180 + Math.random() * 60; this.saturation = 70 + Math.random() * 30; this.brightness = 80 + Math.random() * 20; this.alpha = Math.random() * 0.6 + 0.4; this.baseAlpha = this.alpha; this.frequencyBand = Math.floor(Math.random() * 16); // Assign to frequency band this.pulsePhase = Math.random() * Math.PI * 2; this.trail = []; } update(frequencyData, bassLevel, trebbleLevel, volume) { // Frequency-specific response const myFrequency = frequencyData[this.frequencyBand] || 0; const frequencyResponse = myFrequency / 255; // Bass response affects movement const bassResponse = bassLevel * 3; this.speedX = this.baseSpeedX * (1 + bassResponse); this.speedY = this.baseSpeedY * (1 + bassResponse); // Trebble affects size and color const trebbleResponse = trebbleLevel * 2; this.size = this.baseSize * (1 + frequencyResponse * 4 + trebbleResponse); // Volume affects alpha and hue shift this.alpha = Math.min(1, this.baseAlpha * (0.5 + volume * 0.8)); this.hue = (180 + Math.random() * 60 + trebbleResponse * 30) % 360; // Pulse effect based on frequency this.pulsePhase += 0.1 + frequencyResponse * 0.3; const pulse = Math.sin(this.pulsePhase) * frequencyResponse * 0.5; this.size += pulse; // Movement this.x += this.speedX; this.y += this.speedY; // Trail effect this.trail.push({ x: this.x, y: this.y, alpha: this.alpha }); if (this.trail.length > this.maxTrailLength) { this.trail.shift(); } // Boundary collision with energy transfer if (this.x < 0 || this.x > this.canvas.width) { this.speedX *= -0.8; this.x = Math.max(0, Math.min(this.canvas.width, this.x)); } if (this.y < 0 || this.y > this.canvas.height) { this.speedY *= -0.8; this.y = Math.max(0, Math.min(this.canvas.height, this.y)); } } draw() { // Draw trail this.trail.forEach((point, index) => { const trailAlpha = (index / this.trail.length) * point.alpha * 0.3; this.ctx.beginPath(); this.ctx.globalAlpha = trailAlpha; this.ctx.fillStyle = `hsla(${this.hue}, ${this.saturation}%, ${this.brightness}%, 1)`; this.ctx.arc(point.x, point.y, this.size * 0.3, 0, Math.PI * 2); this.ctx.fill(); }); // Draw main particle this.ctx.beginPath(); this.ctx.globalAlpha = this.alpha; const gradient = this.ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size * 3); gradient.addColorStop(0, `hsla(${this.hue}, ${this.saturation}%, ${this.brightness}%, 1)`); gradient.addColorStop(0.3, `hsla(${this.hue}, ${this.saturation}%, ${this.brightness}%, 0.6)`); gradient.addColorStop(1, `hsla(${this.hue}, ${this.saturation}%, ${this.brightness}%, 0)`); this.ctx.fillStyle = gradient; this.ctx.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2); this.ctx.fill(); // Inner glow this.ctx.beginPath(); this.ctx.globalAlpha = this.alpha * 0.8; this.ctx.fillStyle = `hsla(${this.hue + 20}, 90%, 90%, 1)`; this.ctx.arc(this.x, this.y, this.size * 0.8, 0, Math.PI * 2); this.ctx.fill(); } } class FrequencyVisualizer { constructor(canvas, player) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.player = player; this.ripples = []; // Store ripple effects this.lastBassTime = 0; } drawFrequencyBars(frequencyData, mouse = { x: -1000, y: -1000 }) { const totalBars = Math.min(frequencyData.length, 64); const barSpacing = this.canvas.width / totalBars; const barWidth = barSpacing * 0.7; const centerY = this.canvas.height / 2; const maxBarHeight = this.canvas.height * 0.4; // Enhanced audio analysis const avgVolume = frequencyData.reduce((sum, val) => sum + val, 0) / frequencyData.length / 255; const bassRange = Math.floor(totalBars * 0.2); // 20% for bass const midRange = Math.floor(totalBars * 0.6); // 60% for mids const trebbleRange = totalBars - bassRange - midRange; // 20% for treble const bassLevel = frequencyData.slice(0, bassRange).reduce((sum, val) => sum + val, 0) / bassRange / 255; const midLevel = frequencyData.slice(bassRange, bassRange + midRange).reduce((sum, val) => sum + val, 0) / midRange / 255; const trebbleLevel = frequencyData.slice(bassRange + midRange).reduce((sum, val) => sum + val, 0) / trebbleRange / 255; // Dynamic background color based on frequency balance const bgGradient = this.ctx.createLinearGradient(0, 0, 0, this.canvas.height); const bgHue = 200 + bassLevel * 30 - trebbleLevel * 20; const bgSaturation = 25 + midLevel * 15; bgGradient.addColorStop(0, `hsla(${bgHue}, ${bgSaturation}%, ${4 + avgVolume * 8}%, 0.12)`); bgGradient.addColorStop(0.5, `hsla(${bgHue + 15}, ${bgSaturation + 5}%, ${3 + avgVolume * 6}%, 0.08)`); bgGradient.addColorStop(1, `hsla(${bgHue + 30}, ${bgSaturation + 10}%, ${2 + avgVolume * 4}%, 0.06)`); this.ctx.fillStyle = bgGradient; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Enhanced star field this.drawStarField(avgVolume, trebbleLevel); // Bass ripple effects if (bassLevel > 0.6) { this.createBassRipples(bassLevel); } this.drawRipples(); // High frequency sparkle effects if (trebbleLevel > 0.6) { this.drawSparkles(trebbleLevel); } for (let i = 0; i < totalBars; i++) { const barHeight = (frequencyData[i] / 255) * maxBarHeight; const x = i * barSpacing + (barSpacing - barWidth) / 2; // Dynamic bar width based on frequency content let dynamicBarWidth = barWidth; // Bass bars get wider during heavy bass if (i < bassRange) { const bassIntensity = (frequencyData[i] / 255) * bassLevel; dynamicBarWidth = barWidth * (1 + bassIntensity * 0.4); // Up to 40% wider } // Mid frequency bars get slight width boost during active mids else if (i < bassRange + midRange) { const midIntensity = (frequencyData[i] / 255) * midLevel; dynamicBarWidth = barWidth * (1 + midIntensity * 0.15); // Up to 15% wider } // Treble bars stay normal width for clean high frequency representation // Smooth width animation to prevent jarring changes const targetWidth = dynamicBarWidth; const currentWidth = this.barWidths && this.barWidths[i] ? this.barWidths[i] : barWidth; const smoothWidth = currentWidth + (targetWidth - currentWidth) * 0.12; // Store for next frame if (!this.barWidths) this.barWidths = new Array(totalBars).fill(barWidth); this.barWidths[i] = smoothWidth; // Adjust x position to keep bars centered as they expand const widthDifference = smoothWidth - barWidth; const adjustedX = x - widthDifference / 2; // Mouse interaction - calculate distance from mouse to bar center const barCenterX = adjustedX + smoothWidth / 2; const barCenterY = centerY; const mouseDistance = Math.sqrt( Math.pow(mouse.x - barCenterX, 2) + Math.pow(mouse.y - barCenterY, 2) ); const maxInteractionDistance = 80; const mouseInfluence = Math.max(0, 1 - (mouseDistance / maxInteractionDistance)); // Mouse hover effects const hoverBrightness = mouseInfluence * 25; // Brighten bars near mouse const hoverSize = 1 + mouseInfluence * 0.3; // Slightly enlarge bars near mouse const hoverGlow = mouseInfluence * 0.5; // Extra glow near mouse // Frequency-based color zones let hue, saturation, baseLightness; const normalizedPos = i / totalBars; if (i < bassRange) { // Bass: Warm colors (red-orange) hue = 15 + (i / bassRange) * 45; // 15° to 60° (red to orange) saturation = 70 + bassLevel * 20; baseLightness = 45 + bassLevel * 25; } else if (i < bassRange + midRange) { // Mids: Green-cyan colors const midPos = (i - bassRange) / midRange; hue = 120 + midPos * 60; // 120° to 180° (green to cyan) saturation = 65 + midLevel * 25; baseLightness = 50 + midLevel * 20; } else { // Treble: Blue-purple colors const trebblePos = (i - bassRange - midRange) / trebbleRange; hue = 220 + trebblePos * 60; // 220° to 280° (blue to purple) saturation = 75 + trebbleLevel * 20; baseLightness = 55 + trebbleLevel * 25; } // Smooth bar animation const targetHeight = barHeight; const currentHeight = this.barHeights && this.barHeights[i] ? this.barHeights[i] : 0; const smoothHeight = currentHeight + (targetHeight - currentHeight) * 0.18; if (!this.barHeights) this.barHeights = new Array(totalBars).fill(0); this.barHeights[i] = smoothHeight; // Dynamic saturation based on volume and mouse interaction const volumeBoost = (frequencyData[i] / 255); saturation = Math.min(95, saturation + volumeBoost * 15 + hoverBrightness * 0.5); const lightness = Math.min(85, baseLightness + volumeBoost * 20 + hoverBrightness); // Apply mouse hover size effect const finalHeight = smoothHeight * hoverSize; const finalWidth = smoothWidth * hoverSize; const sizeAdjustX = adjustedX - (finalWidth - smoothWidth) / 2; // Enhanced vibration effect with more natural movement const vibrationX = bassLevel > 0.65 ? Math.sin(Date.now() * 0.015 + i * 0.3) * bassLevel * 1.5 : 0; const vibrationY = bassLevel > 0.65 ? Math.cos(Date.now() * 0.012 + i * 0.4) * bassLevel * 0.8 : 0; const vibratedX = sizeAdjustX + vibrationX; const vibratedY = centerY + vibrationY; // Enhanced glow effect with frequency-specific colors and mouse interaction const glowIntensity = volumeBoost * (0.7 + avgVolume * 0.5) + hoverGlow; if (glowIntensity > 0.15) { this.ctx.shadowColor = `hsl(${hue}, ${Math.min(saturation + 10, 100)}%, ${Math.min(lightness + 15, 90)}%)`; this.ctx.shadowBlur = 12 + glowIntensity * 35; this.ctx.shadowOffsetX = 0; this.ctx.shadowOffsetY = 0; } // Enhanced 5-stop gradient const gradient = this.ctx.createLinearGradient(vibratedX, vibratedY - finalHeight/2, vibratedX, vibratedY + finalHeight/2); gradient.addColorStop(0, `hsl(${hue + 5}, ${Math.min(saturation + 15, 100)}%, ${Math.min(lightness + 30, 90)}%)`); gradient.addColorStop(0.2, `hsl(${hue}, ${saturation + 10}%, ${lightness + 15}%)`); gradient.addColorStop(0.5, `hsl(${hue}, ${saturation}%, ${lightness}%)`); gradient.addColorStop(0.8, `hsl(${hue - 5}, ${Math.max(saturation - 10, 40)}%, ${Math.max(lightness - 15, 25)}%)`); gradient.addColorStop(1, `hsl(${hue - 10}, ${Math.max(saturation - 20, 30)}%, ${Math.max(lightness - 25, 15)}%)`); // Draw main bar with dynamic width and mouse effects this.ctx.fillStyle = gradient; this.ctx.beginPath(); const radius = Math.min(finalWidth * 0.12, 4); // Scale radius with width const topY = vibratedY - finalHeight / 2; const bottomY = vibratedY + finalHeight / 2; this.ctx.moveTo(vibratedX + radius, topY); this.ctx.lineTo(vibratedX + finalWidth - radius, topY); this.ctx.quadraticCurveTo(vibratedX + finalWidth, topY, vibratedX + finalWidth, topY + radius); this.ctx.lineTo(vibratedX + finalWidth, bottomY - radius); this.ctx.quadraticCurveTo(vibratedX + finalWidth, bottomY, vibratedX + finalWidth - radius, bottomY); this.ctx.lineTo(vibratedX + radius, bottomY); this.ctx.quadraticCurveTo(vibratedX, bottomY, vibratedX, bottomY - radius); this.ctx.lineTo(vibratedX, topY + radius); this.ctx.quadraticCurveTo(vibratedX, topY, vibratedX + radius, topY); this.ctx.closePath(); this.ctx.fill(); // Reset shadow for other elements this.ctx.shadowBlur = 0; // Enhanced reflection with frequency-specific colors if (finalHeight > 8) { const reflectionHeight = finalHeight * 0.65; const reflectionY = vibratedY + finalHeight / 2 + 3; const reflectionGradient = this.ctx.createLinearGradient( vibratedX, reflectionY, vibratedX, reflectionY + reflectionHeight ); reflectionGradient.addColorStop(0, `hsla(${hue}, ${saturation}%, ${lightness}%, 0.35)`); reflectionGradient.addColorStop(0.3, `hsla(${hue}, ${saturation}%, ${lightness}%, 0.2)`); reflectionGradient.addColorStop(0.7, `hsla(${hue}, ${saturation}%, ${lightness}%, 0.1)`); reflectionGradient.addColorStop(1, `hsla(${hue}, ${saturation}%, ${lightness}%, 0)`); this.ctx.fillStyle = reflectionGradient; this.ctx.beginPath(); this.ctx.moveTo(vibratedX + radius, reflectionY + reflectionHeight); this.ctx.lineTo(vibratedX + finalWidth - radius, reflectionY + reflectionHeight); this.ctx.quadraticCurveTo(vibratedX + finalWidth, reflectionY + reflectionHeight, vibratedX + finalWidth, reflectionY + reflectionHeight - radius); this.ctx.lineTo(vibratedX + finalWidth, reflectionY + radius); this.ctx.quadraticCurveTo(vibratedX + finalWidth, reflectionY, vibratedX + finalWidth - radius, reflectionY); this.ctx.lineTo(vibratedX + radius, reflectionY); this.ctx.quadraticCurveTo(vibratedX, reflectionY, vibratedX, reflectionY + radius); this.ctx.lineTo(vibratedX, reflectionY + reflectionHeight - radius); this.ctx.quadraticCurveTo(vibratedX, reflectionY + reflectionHeight, vibratedX + radius, reflectionY + reflectionHeight); this.ctx.closePath(); this.ctx.fill(); } // Enhanced inner highlight with frequency colors and dynamic width if (finalHeight > 12) { this.ctx.fillStyle = `hsla(${hue + 10}, ${Math.min(saturation + 20, 100)}%, 90%, 0.45)`; this.ctx.fillRect(vibratedX + 1, vibratedY - finalHeight/2 + 1, Math.max(finalWidth - 2, 1), Math.max(finalHeight/3.5, 1)); } // High frequency particle burst if (i >= bassRange + midRange && volumeBoost > 0.75 && trebbleLevel > 0.7) { this.drawParticleBurst(vibratedX + finalWidth/2, topY, hue, volumeBoost); } } } drawStarField(intensity, trebbleLevel) { const baseStarCount = 40; const starCount = Math.floor(baseStarCount + intensity * 35 + trebbleLevel * 20); const time = Date.now() * 0.001; for (let i = 0; i < starCount; i++) { const x = (i * 1234.5) % this.canvas.width; const y = (i * 2345.6) % this.canvas.height; const baseSize = 0.4 + (i % 4) * 0.3; const size = Math.max(0.1, baseSize); // Ensure positive size const twinkleSpeed = 0.003 + (i % 10) * 0.0005; const twinkle = Math.sin(time * twinkleSpeed + i) * 0.5 + 0.5; // Color variation based on position const starHue = 180 + (i % 60) + trebbleLevel * 30; const alpha = Math.max(0, (0.08 + intensity * twinkle * 0.4) * (0.7 + trebbleLevel * 0.5)); this.ctx.fillStyle = `hsla(${starHue}, 70%, 85%, ${alpha})`; this.ctx.beginPath(); this.ctx.arc(x, y, size, 0, Math.PI * 2); this.ctx.fill(); } } createBassRipples(bassLevel) { const currentTime = Date.now(); // Create new ripple every 200ms minimum to avoid overwhelming if (currentTime - this.lastBassTime > 200 && bassLevel > 0.65) { const centerX = this.canvas.width / 2; const centerY = this.canvas.height / 2; // Add some randomness to ripple origin for variety const offsetX = (Math.random() - 0.5) * 100; const offsetY = (Math.random() - 0.5) * 60; this.ripples.push({ x: centerX + offsetX, y: centerY + offsetY, radius: 0, maxRadius: Math.min(this.canvas.width, this.canvas.height) * 0.8, intensity: bassLevel, life: 1, speed: 2 + bassLevel * 3, // Faster expansion for stronger bass color: { hue: 15 + Math.random() * 30, // Warm bass colors saturation: 70 + bassLevel * 20, lightness: 40 + bassLevel * 20 } }); this.lastBassTime = currentTime; } } drawRipples() { for (let i = this.ripples.length - 1; i >= 0; i--) { const ripple = this.ripples[i]; // Update ripple ripple.radius += ripple.speed; ripple.life -= 0.008; // Slower fade for more visible effect // Remove dead ripples if (ripple.life <= 0 || ripple.radius > ripple.maxRadius) { this.ripples.splice(i, 1); continue; } // Draw multiple concentric rings for richer effect for (let ring = 0; ring < 3; ring++) { const ringRadius = Math.max(0.1, ripple.radius - ring * 15); const ringAlpha = Math.max(0, ripple.life * ripple.intensity * (0.3 - ring * 0.08)); if (ringRadius > 0 && ringAlpha > 0) { this.ctx.strokeStyle = `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness}%, ${ringAlpha})`; this.ctx.lineWidth = 2 + ring * 0.5; this.ctx.beginPath(); this.ctx.arc(ripple.x, ripple.y, ringRadius, 0, Math.PI * 2); this.ctx.stroke(); } } // Add inner glow effect if (ripple.radius < 50) { const glowAlpha = Math.max(0, ripple.life * ripple.intensity * 0.15); if (glowAlpha > 0) { const glowGradient = this.ctx.createRadialGradient( ripple.x, ripple.y, 0, ripple.x, ripple.y, ripple.radius + 20 ); glowGradient.addColorStop(0, `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness + 20}%, ${glowAlpha})`); glowGradient.addColorStop(1, `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness + 20}%, 0)`); this.ctx.fillStyle = glowGradient; this.ctx.beginPath(); this.ctx.arc(ripple.x, ripple.y, ripple.radius + 20, 0, Math.PI * 2); this.ctx.fill(); } } } } drawSparkles(trebbleLevel) { const sparkleCount = Math.floor(trebbleLevel * 15); const time = Date.now() * 0.002; for (let i = 0; i < sparkleCount; i++) { const x = (Math.sin(time + i * 2.5) * 0.5 + 0.5) * this.canvas.width; const y = (Math.cos(time + i * 3.1) * 0.3 + 0.2) * this.canvas.height; const baseSize = 1 + Math.sin(time * 2 + i) * 1.5; const size = Math.max(0.1, baseSize); // Ensure positive size const brightness = trebbleLevel * (0.7 + Math.sin(time * 3 + i) * 0.3); this.ctx.fillStyle = `hsla(${45 + i * 20}, 90%, 90%, ${brightness})`; this.ctx.beginPath(); this.ctx.arc(x, y, size, 0, Math.PI * 2); this.ctx.fill(); // Cross sparkle effect with safe line length const lineLength = Math.max(1, size * 2); this.ctx.strokeStyle = `hsla(${45 + i * 20}, 90%, 95%, ${brightness * 0.8})`; this.ctx.lineWidth = 0.5; this.ctx.beginPath(); this.ctx.moveTo(x - lineLength, y); this.ctx.lineTo(x + lineLength, y); this.ctx.moveTo(x, y - lineLength); this.ctx.lineTo(x, y + lineLength); this.ctx.stroke(); } } drawParticleBurst(x, y, hue, intensity) { if (!this.particles) this.particles = []; // Create new particles if (Math.random() < intensity * 0.3) { for (let i = 0; i < 3; i++) { this.particles.push({ x: x + (Math.random() - 0.5) * 10, y: y + (Math.random() - 0.5) * 5, vx: (Math.random() - 0.5) * 4, vy: -Math.random() * 3 - 1, life: 1, maxLife: 0.8 + Math.random() * 0.4, hue: hue + (Math.random() - 0.5) * 30, size: 1 + Math.random() * 2 }); } } // Update and draw existing particles for (let i = this.particles.length - 1; i >= 0; i--) { const p = this.particles[i]; p.x += p.vx; p.y += p.vy; p.vy += 0.1; // gravity p.life -= 0.02; if (p.life <= 0) { this.particles.splice(i, 1); continue; } const alpha = p.life / p.maxLife; const radius = Math.max(0.1, p.size * alpha); // Ensure positive radius this.ctx.fillStyle = `hsla(${p.hue}, 90%, 80%, ${alpha})`; this.ctx.beginPath(); this.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2); this.ctx.fill(); } } drawWaveform(frequencyData) { const sliceWidth = this.canvas.width / frequencyData.length; let x = 0; this.ctx.beginPath(); this.ctx.lineWidth = 3; this.ctx.strokeStyle = 'rgba(0, 247, 255, 0.8)'; for (let i = 0; i < frequencyData.length; i++) { const v = frequencyData[i] / 128.0; const y = v * this.canvas.height / 2; if (i === 0) { this.ctx.moveTo(x, y); } else { this.ctx.lineTo(x, y); } x += sliceWidth; } this.ctx.stroke(); } drawCircularSpectrum(frequencyData) { const centerX = this.canvas.width / 2; const centerY = this.canvas.height / 2; const radius = Math.min(centerX, centerY) * 0.3; const angleStep = (Math.PI * 2) / frequencyData.length; for (let i = 0; i < frequencyData.length; i++) { const angle = i * angleStep; const barHeight = (frequencyData[i] / 255) * 150; const hue = (i / frequencyData.length) * 360; const x1 = centerX + Math.cos(angle) * radius; const y1 = centerY + Math.sin(angle) * radius; const x2 = centerX + Math.cos(angle) * (radius + barHeight); const y2 = centerY + Math.sin(angle) * (radius + barHeight); this.ctx.beginPath(); this.ctx.strokeStyle = `hsla(${hue}, 80%, 60%, 0.8)`; this.ctx.lineWidth = 3; this.ctx.moveTo(x1, y1); this.ctx.lineTo(x2, y2); this.ctx.stroke(); } } } class EnhancedParticleSystem { constructor(particleCanvasId, frequencyCanvasId, player) { this.particleCanvas = document.getElementById(particleCanvasId); this.frequencyCanvas = document.getElementById(frequencyCanvasId); this.particleCtx = this.particleCanvas.getContext('2d'); this.frequencyCtx = this.frequencyCanvas.getContext('2d'); this.player = player; this.particles = []; this.particleCount = 100; this.visualMode = 1; // Always use frequency bars this.frequencyVisualizer = new FrequencyVisualizer(this.frequencyCanvas, player); this.mouse = { x: 0, y: 0, isPressed: false }; this.clickRipples = []; this.init(); } init() { window.addEventListener('resize', () => this.resize(), false); this.resize(); for (let i = 0; i < this.particleCount; i++) { this.particles.push(new EnhancedParticle(this.particleCanvas)); } // Mouse event listeners this.frequencyCanvas.addEventListener('mousemove', (e) => { const rect = this.frequencyCanvas.getBoundingClientRect(); this.mouse.x = e.clientX - rect.left; this.mouse.y = e.clientY - rect.top; }); this.frequencyCanvas.addEventListener('mousedown', (e) => { this.mouse.isPressed = true; this.createClickRipple(this.mouse.x, this.mouse.y); }); this.frequencyCanvas.addEventListener('mouseup', () => { this.mouse.isPressed = false; }); this.frequencyCanvas.addEventListener('mouseleave', () => { this.mouse.x = -1000; // Move mouse off-screen this.mouse.y = -1000; this.mouse.isPressed = false; }); requestAnimationFrame(() => this.animate()); } resize() { this.particleCanvas.width = window.innerWidth; this.particleCanvas.height = window.innerHeight; this.frequencyCanvas.width = window.innerWidth; this.frequencyCanvas.height = window.innerHeight; } toggleMode() { this.visualMode = (this.visualMode + 1) % 4; const modes = ['Particles', 'Frequency Bars', 'Waveform', 'Circular Spectrum']; document.getElementById('audioInfo').querySelector('div').textContent = `Mode: ${modes[this.visualMode]}`; } animate() { // Clear canvases this.particleCtx.clearRect(0, 0, this.particleCanvas.width, this.particleCanvas.height); this.frequencyCtx.clearRect(0, 0, this.frequencyCanvas.width, this.frequencyCanvas.height); const analyser = this.player.getAnalyser(); let audioData = { frequencyData: new Uint8Array(128), bassLevel: 0, trebbleLevel: 0, volume: 0, dominantFreq: 0 }; if (analyser) { const bufferLength = analyser.frequencyBinCount; const frequencyData = new Uint8Array(bufferLength); analyser.getByteFrequencyData(frequencyData); // Downsample for performance const downsampledData = new Uint8Array(128); const step = Math.floor(bufferLength / 128); for (let i = 0; i < 128; i++) { downsampledData[i] = frequencyData[i * step] || 0; } // Calculate audio metrics const bassEnd = Math.floor(128 * 0.15); const trebbleStart = Math.floor(128 * 0.7); audioData.frequencyData = downsampledData; audioData.bassLevel = this.getAverageLevel(downsampledData, 0, bassEnd) / 255; audioData.trebbleLevel = this.getAverageLevel(downsampledData, trebbleStart, 128) / 255; audioData.volume = this.getAverageLevel(downsampledData, 0, 128) / 255; audioData.dominantFreq = this.getDominantFrequency(downsampledData); // Update UI document.getElementById('volumeLevel').textContent = Math.round(audioData.volume * 100); document.getElementById('dominantFreq').textContent = Math.round(audioData.dominantFreq); } // Always render frequency bars with mouse interaction this.frequencyVisualizer.drawFrequencyBars(audioData.frequencyData, this.mouse); // Draw click ripples this.drawClickRipples(); requestAnimationFrame(() => this.animate()); } getAverageLevel(data, start, end) { let sum = 0; for (let i = start; i < end; i++) { sum += data[i]; } return sum / (end - start); } getDominantFrequency(data) { let maxIndex = 0; let maxValue = 0; for (let i = 0; i < data.length; i++) { if (data[i] > maxValue) { maxValue = data[i]; maxIndex = i; } } // Convert bin to frequency (approximate) const nyquist = 22050; // Assuming 44.1kHz sample rate return (maxIndex / data.length) * nyquist; } createClickRipple(x, y) { this.clickRipples.push({ x: x, y: y, radius: 0, maxRadius: 200, life: 1, speed: 4, color: { hue: 200 + Math.random() * 60, saturation: 80, lightness: 70 } }); } drawClickRipples() { for (let i = this.clickRipples.length - 1; i >= 0; i--) { const ripple = this.clickRipples[i]; ripple.radius += ripple.speed; ripple.life -= 0.015; if (ripple.life <= 0 || ripple.radius > ripple.maxRadius) { this.clickRipples.splice(i, 1); continue; } // Draw ripple const alpha = ripple.life * 0.6; this.frequencyCtx.strokeStyle = `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness}%, ${alpha})`; this.frequencyCtx.lineWidth = 2; this.frequencyCtx.beginPath(); this.frequencyCtx.arc(ripple.x, ripple.y, ripple.radius, 0, Math.PI * 2); this.frequencyCtx.stroke(); // Inner glow if (ripple.radius < 50) { const glowAlpha = ripple.life * 0.3; const glowGradient = this.frequencyCtx.createRadialGradient( ripple.x, ripple.y, 0, ripple.x, ripple.y, ripple.radius + 15 ); glowGradient.addColorStop(0, `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness + 10}%, ${glowAlpha})`); glowGradient.addColorStop(1, `hsla(${ripple.color.hue}, ${ripple.color.saturation}%, ${ripple.color.lightness + 10}%, 0)`); this.frequencyCtx.fillStyle = glowGradient; this.frequencyCtx.beginPath(); this.frequencyCtx.arc(ripple.x, ripple.y, ripple.radius + 15, 0, Math.PI * 2); this.frequencyCtx.fill(); } } } } document.addEventListener('DOMContentLoaded', () => { const ordinalsPlayer = new OrdinalsAudioPlayer(); const particleSystem = new EnhancedParticleSystem('particle-canvas', 'frequency-canvas', ordinalsPlayer); const ordinalsToggle = document.getElementById('ordinalsToggle'); ordinalsToggle.addEventListener('click', async () => { if (ordinalsPlayer.getLoadingState()) return; const isMuted = ordinalsToggle.classList.toggle('muted'); ordinalsToggle.classList.toggle('playing', !isMuted); if (isMuted) { ordinalsPlayer.stop(); } else { ordinalsToggle.classList.add('loading'); try { await ordinalsPlayer.play(); } catch (error) { console.error('Failed to play audio:', error); ordinalsToggle.classList.remove('playing'); ordinalsToggle.classList.add('muted'); } finally { ordinalsToggle.classList.remove('loading'); } } }); document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden' && ordinalsPlayer.isPlaying) { ordinalsPlayer.pause(); ordinalsToggle.classList.remove('playing'); ordinalsToggle.classList.add('muted'); } }); }); </script> </body> </html></body><html>hj"1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5SETapp3D Orditypeordname nezumi bar royaltiesLX[{"type":"address","destination":"1AwjKNmoFkZJQbyAUSW85jvFoM8XWPDNcA","percentage":0.2}]
    https://whatsonchain.com/tx/undefined

1 Output

Total Output:
0.00000000 BSV
  • j"1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5SETapp 1sat.markettypeordopburn
    https://whatsonchain.com/tx/90a542bc03e33a59b67cf6922b4ea34bf93fda7b528cf8a3a476b5458fac17c3