Transaction

a76da7d2e4e5b1ddc710ea04f38e3c4e2e5d95087cfe6d5aa6509a40176d53fb
2025-07-13 02:59:20
0.00002572 BSV
(
0.02049240 BSV
-
0.02046668 BSV
)
44.82 sat/KB
1
5,385
57,374 B

3 Outputs

Total Output:
0.02046668 BSV
  • v© £üaÍïêjj&@Ú:‹ø'šÀ_ψ¬cordQ text/htmlM/Ý<!DOCTYPE html> <html lang="ja"> <head> <title id="page-title">Cosmic Rainbow</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0"> <style> body { margin: 0; background-color: #000000; overscroll-behavior: none; cursor: none; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; touch-action: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } canvas { display: block; touch-action: none; } .sound-overlay { position: fixed; top: 20px; right: 20px; z-index: 100; opacity: 0.3; transition: opacity 0.3s ease; display: flex; flex-direction: column; align-items: center; gap: 8px; } .sound-overlay:hover { opacity: 1; } @media (hover: none) and (pointer: coarse) { .sound-overlay { opacity: 0.5; } .sound-overlay:active, .sound-overlay.touched { opacity: 1; } .sound-overlay.fade-out { opacity: 0.5; transition: opacity 1s ease; } } .sound-info { font-size: 12px; text-align: center; cursor: pointer; padding: 10px 18px; margin: 0; border-radius: 8px; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; user-select: none; min-width: 80px; white-space: nowrap; min-height: 24px; display: flex; align-items: center; justify-content: center; -webkit-tap-highlight-color: transparent; } .sound-info.sound-on { /* Dynamic colors will be set by JavaScript */ } .sound-info.sound-on:hover { transform: translateY(-1px); } .sound-info.sound-on:active { transform: translateY(0px); } .sound-info.sound-on.compact { font-size: 0px; padding: 10px; border-radius: 50%; min-width: 20px; width: 20px; height: 20px; min-height: 28px; } .sound-info.sound-on.compact::after { content: '●'; font-size: 12px; line-height: 1; } .sound-info.sound-off { color: rgba(150, 150, 150, 0.8); background: linear-gradient(135deg, rgba(100, 100, 100, 0.1) 0%, rgba(120, 120, 120, 0.05) 100%); border: 1px solid rgba(100, 100, 100, 0.3); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .sound-info.sound-off:hover { background: linear-gradient(135deg, rgba(100, 100, 100, 0.2) 0%, rgba(120, 120, 120, 0.15) 100%); border-color: rgba(150, 150, 150, 0.5); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); transform: translateY(-1px); } .sound-info.sound-off:active { transform: translateY(0px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); } .sound-info.sound-on::before { content: ''; position: absolute; top: -1px; left: -1px; right: -1px; bottom: -1px; border-radius: 9px; background-size: 300% 300%; animation: colorGlow 4s ease-in-out infinite; opacity: 0; transition: opacity 0.3s ease; z-index: -1; } .sound-info.sound-on:hover::before { opacity: 0.6; } @keyframes colorGlow { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } } @media (max-width: 768px) { .sound-overlay { top: 15px; right: 15px; } .sound-info { font-size: 11px; padding: 8px 14px; min-height: 20px; min-width: 70px; } .sound-info.sound-on.compact { width: 18px; height: 18px; min-height: 26px; min-width: 26px; padding: 8px; } .sound-info.sound-on.compact::after { font-size: 10px; } } @media (max-width: 480px) { .sound-overlay { top: 10px; right: 10px; } .sound-info { font-size: 10px; padding: 6px 12px; min-height: 18px; min-width: 60px; } .sound-info.sound-on.compact { width: 16px; height: 16px; min-height: 24px; min-width: 24px; padding: 6px; } .sound-info.sound-on.compact::after { font-size: 8px; } } </style> </head> <body> <div class="sound-overlay" id="sound-overlay"> <div class="sound-info sound-off" id="sound-info">Sound: OFF</div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // ======================================== // COSMIC COLLECTION PIECE SELECTOR // Change this number (1-5) to create different pieces: // 1: Cosmic Blue, 2: Cosmic White, 3: Cosmic Red, 4: Cosmic Gold, 5: Cosmic Rainbow // ======================================== const COSMIC_PIECE = 5; // ← CHANGE THIS NUMBER (1-5) // Cosmic Collection Configuration - Harmonious Edition const COSMIC_THEMES = { 1: { name: "Cosmic Blue", title: "Cosmic Blue - Interactive Particle Art", hueRange: [0.55, 0.65], // Blue spectrum saturation: 0.9, lightness: 0.7, scaleType: "pentatonic_major", chordType: "major", baseFrequency: 260, tempo: 90, colorMode: "static", uiColors: { primary: "rgba(100, 150, 255, 0.95)", background: "linear-gradient(135deg, rgba(100, 150, 255, 0.2) 0%, rgba(120, 180, 255, 0.15) 50%, rgba(140, 200, 255, 0.1) 100%)", border: "rgba(120, 180, 255, 0.4)", shadow: "rgba(100, 150, 255, 0.3)", glow: "linear-gradient(45deg, rgba(100, 150, 255, 0.4) 0%, rgba(120, 180, 255, 0.3) 25%, rgba(140, 200, 255, 0.2) 50%, rgba(120, 180, 255, 0.3) 75%, rgba(100, 150, 255, 0.4) 100%)" } }, 2: { name: "Cosmic White", title: "Cosmic White - Interactive Particle Art", hueRange: [0, 1], // Any hue saturation: 0.1, // Low saturation for white lightness: 0.9, // High lightness for white scaleType: "major", chordType: "major", baseFrequency: 330, tempo: 95, colorMode: "static", uiColors: { primary: "rgba(240, 240, 255, 0.95)", background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(240, 240, 255, 0.15) 50%, rgba(220, 220, 255, 0.1) 100%)", border: "rgba(255, 255, 255, 0.5)", shadow: "rgba(255, 255, 255, 0.3)", glow: "linear-gradient(45deg, rgba(255, 255, 255, 0.4) 0%, rgba(240, 240, 255, 0.3) 25%, rgba(220, 220, 255, 0.2) 50%, rgba(240, 240, 255, 0.3) 75%, rgba(255, 255, 255, 0.4) 100%)" } }, 3: { name: "Cosmic Red", title: "Cosmic Red - Interactive Particle Art", hueRange: [0.95, 0.05], // Red spectrum (wrapping around hue wheel) saturation: 0.8, lightness: 0.6, scaleType: "dorian", chordType: "sus4", baseFrequency: 280, tempo: 85, colorMode: "static", uiColors: { primary: "rgba(255, 120, 120, 0.95)", background: "linear-gradient(135deg, rgba(255, 100, 100, 0.2) 0%, rgba(255, 140, 140, 0.15) 50%, rgba(255, 180, 180, 0.1) 100%)", border: "rgba(255, 120, 120, 0.4)", shadow: "rgba(255, 80, 80, 0.3)", glow: "linear-gradient(45deg, rgba(255, 80, 80, 0.4) 0%, rgba(255, 120, 120, 0.3) 25%, rgba(255, 160, 160, 0.2) 50%, rgba(255, 120, 120, 0.3) 75%, rgba(255, 80, 80, 0.4) 100%)" } }, 4: { name: "Cosmic Gold", title: "Cosmic Gold - Interactive Particle Art", hueRange: [0.15, 0.2], // Yellow-Gold saturation: 0.8, lightness: 0.7, scaleType: "blues_pentatonic", chordType: "add9", baseFrequency: 400, tempo: 105, colorMode: "static", uiColors: { primary: "rgba(255, 215, 100, 0.95)", background: "linear-gradient(135deg, rgba(255, 200, 50, 0.2) 0%, rgba(255, 215, 100, 0.15) 50%, rgba(255, 230, 150, 0.1) 100%)", border: "rgba(255, 215, 100, 0.4)", shadow: "rgba(255, 180, 50, 0.3)", glow: "linear-gradient(45deg, rgba(255, 180, 50, 0.4) 0%, rgba(255, 215, 100, 0.3) 25%, rgba(255, 230, 150, 0.2) 50%, rgba(255, 215, 100, 0.3) 75%, rgba(255, 180, 50, 0.4) 100%)" } }, 5: { name: "Cosmic Rainbow", title: "Cosmic Rainbow - Interactive Particle Art", hueRange: [0, 1], // Full spectrum saturation: 0.8, lightness: 0.7, scaleType: "pentatonic_minor", chordType: "maj7", baseFrequency: 350, tempo: 100, colorMode: "rainbow", // Special mode for rainbow cycling uiColors: { primary: "rgba(255, 150, 255, 0.95)", background: "linear-gradient(135deg, rgba(255, 100, 150, 0.2) 0%, rgba(150, 255, 150, 0.15) 50%, rgba(150, 150, 255, 0.1) 100%)", border: "rgba(200, 150, 255, 0.4)", shadow: "rgba(255, 100, 200, 0.3)", glow: "linear-gradient(45deg, rgba(255, 100, 150, 0.4) 0%, rgba(150, 255, 100, 0.3) 25%, rgba(100, 150, 255, 0.2) 50%, rgba(255, 150, 100, 0.3) 75%, rgba(150, 100, 255, 0.4) 100%)" } } }; // Get current theme const CURRENT_THEME = COSMIC_THEMES[COSMIC_PIECE]; // Update page title document.getElementById('page-title').textContent = CURRENT_THEME.title; document.title = CURRENT_THEME.title; // Apply theme UI colors function applyThemeColors() { const style = document.createElement('style'); style.textContent = ` .sound-info.sound-on { color: ${CURRENT_THEME.uiColors.primary}; background: ${CURRENT_THEME.uiColors.background}; border: 1px solid ${CURRENT_THEME.uiColors.border}; box-shadow: 0 2px 12px ${CURRENT_THEME.uiColors.shadow}; } .sound-info.sound-on:hover { background: ${CURRENT_THEME.uiColors.background.replace(/0\.2/g, '0.3').replace(/0\.15/g, '0.25').replace(/0\.1/g, '0.2')}; border-color: ${CURRENT_THEME.uiColors.border.replace('0.4', '0.6')}; box-shadow: 0 4px 20px ${CURRENT_THEME.uiColors.shadow.replace('0.3', '0.4')}; } .sound-info.sound-on:active { box-shadow: 0 2px 12px ${CURRENT_THEME.uiColors.shadow.replace('0.3', '0.5')}; } .sound-info.sound-on.compact::after { color: ${CURRENT_THEME.uiColors.primary}; } .sound-info.sound-on::before { background: ${CURRENT_THEME.uiColors.glow}; } `; document.head.appendChild(style); } // Apply colors on load applyThemeColors(); // Cosmic Phoenix音楽エンジン - Harmonious Edition class CosmicPhoenixMusicEngine { constructor() { this.audioContext = null; this.isInitialized = false; this.isMuted = true; // テーマに基づく音楽パラメータ this.soundParams = { baseFrequency: CURRENT_THEME.baseFrequency, scaleType: CURRENT_THEME.scaleType, chordType: CURRENT_THEME.chordType, tempo: CURRENT_THEME.tempo, reverbAmount: 0.6 }; // 音階定義(心地よい協和音重視) this.availableScales = { pentatonic_major: [1, 9/8, 5/4, 3/2, 5/3, 2, 9/4, 5/2], major: [1, 9/8, 5/4, 4/3, 3/2, 5/3, 15/8, 2], dorian: [1, 9/8, 6/5, 4/3, 3/2, 5/3, 9/5, 2], // 温かみのあるモード blues_pentatonic: [1, 6/5, 4/3, 3/2, 9/5, 2, 12/5, 8/3], pentatonic_minor: [1, 6/5, 4/3, 3/2, 9/5, 2, 12/5, 8/3] // 夢幻的だが安定 }; // コード定義(すべて協和的) this.chordTypes = { major: [1, 5/4, 3/2], sus4: [1, 4/3, 3/2], // 浮遊感があり温かい add9: [1, 5/4, 3/2, 9/4], // 豊かで美しい maj7: [1, 5/4, 3/2, 15/8] // 夢幻的で美しい }; // メロディーパターン this.melodyPatterns = { gentle: [0, 1, 2, 1, 0, 2, 1, 0], flowing: [0, 2, 1, 3, 2, 1, 0], peaceful: [0, 1, 0, 2, 1, 0], warm: [0, 2, 1, 2, 0, 1], // 温かいパターン dreamy: [0, 1, 3, 2, 1, 0, 2] // 夢幻的パターン }; this.currentPattern = 'gentle'; this.patternIndex = 0; // ホイール・ピンチ音響用 this.lastWheelTime = 0; this.wheelCooldown = 150; this.currentZoomLevel = 1000; this.lastPinchDistance = null; // アクティブノード管理 this.activeNodes = { oscillators: [], gains: [], filters: [] }; this.masterGain = null; this.reverb = null; this.compressor = null; this.currentScale = null; this.zoomEQ = null; // シーケンサー this.melodyInterval = null; this.ambientInterval = null; // レインボーモード用 this.rainbowTime = 0; } async initialize() { if (this.audioContext) { await this.audioContext.close(); } this.audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 48000, latencyHint: 'interactive' }); await this.audioContext.resume(); await this.createMasterChain(); await this.createReverb(); this.isInitialized = true; this.updateCurrentScale(); console.log(`${CURRENT_THEME.name} Music Engine initialized (Harmonious Edition)`); } async createMasterChain() { this.compressor = this.audioContext.createDynamicsCompressor(); this.compressor.threshold.value = -20; this.compressor.knee.value = 30; this.compressor.ratio.value = 2.5; this.compressor.attack.value = 0.02; this.compressor.release.value = 0.3; // EQ(より温かいサウンド) this.eqLow = this.audioContext.createBiquadFilter(); this.eqLow.type = 'peaking'; this.eqLow.frequency.value = 150; this.eqLow.Q.value = 0.8; this.eqLow.gain.value = 3; // 少し抑えめ this.eqHigh = this.audioContext.createBiquadFilter(); this.eqHigh.type = 'peaking'; this.eqHigh.frequency.value = 4000; this.eqHigh.Q.value = 0.6; this.eqHigh.gain.value = 1.5; // より自然に // ズーム連動EQ this.zoomEQ = this.audioContext.createBiquadFilter(); this.zoomEQ.type = 'peaking'; this.zoomEQ.frequency.value = 1000; this.zoomEQ.Q.value = 1.0; this.zoomEQ.gain.value = 0; this.masterGain = this.audioContext.createGain(); this.masterGain.gain.value = 0.4; // 少し音量を下げて心地よく this.compressor.connect(this.eqLow); this.eqLow.connect(this.eqHigh); this.eqHigh.connect(this.zoomEQ); this.zoomEQ.connect(this.masterGain); this.masterGain.connect(this.audioContext.destination); } async createReverb() { this.reverb = this.audioContext.createConvolver(); const length = 3 + this.soundParams.reverbAmount * 2; const decay = 2.0; // 少し短めで自然に const sampleRate = this.audioContext.sampleRate; const impulse = this.audioContext.createBuffer(2, length * sampleRate, sampleRate); for (let channel = 0; channel < 2; channel++) { const impulseData = impulse.getChannelData(channel); for (let i = 0; i < impulseData.length; i++) { const t = i / sampleRate; const earlyReflection = Math.exp(-t * 6) * (Math.random() * 2 - 1) * 0.15; const lateReverb = Math.exp(-t * decay) * (Math.random() * 2 - 1) * 0.7; impulseData[i] = (earlyReflection + lateReverb) * (1 - t/length); } } this.reverb.buffer = impulse; this.reverb.connect(this.compressor); } updateCurrentScale() { const baseScale = this.availableScales[this.soundParams.scaleType]; this.currentScale = baseScale.map(ratio => ratio * this.soundParams.baseFrequency); } // ズーム操作音(より心地よく) playZoomSound(zoomDirection, intensity) { if (!this.isInitialized || this.isMuted) return; const currentTime = this.audioContext.currentTime; const now = Date.now(); if (now - this.lastWheelTime < this.wheelCooldown) return; this.lastWheelTime = now; const arpeggioNotes = [0, 1, 2, 3, 4]; const noteOrder = zoomDirection > 0 ? [0, 2, 4] : [4, 2, 0]; const baseVolume = 0.12 + intensity * 0.08; // 音量を抑えめに const duration = 0.8 + intensity * 0.3; noteOrder.forEach((noteIndex, i) => { setTimeout(() => { const scaleIndex = arpeggioNotes[noteIndex]; const frequency = this.currentScale[scaleIndex]; [0, 12, 19].forEach((detune, layerIndex) => { const osc = this.audioContext.createOscillator(); const gain = this.audioContext.createGain(); const filter = this.audioContext.createBiquadFilter(); const panner = this.audioContext.createStereoPanner(); osc.type = 'sine'; osc.frequency.setValueAtTime(frequency, currentTime); osc.detune.setValueAtTime(detune, currentTime); filter.type = 'lowpass'; filter.frequency.setValueAtTime(frequency * 3, currentTime); // より柔らかく filter.Q.setValueAtTime(1.5, currentTime); panner.pan.value = (layerIndex - 1) * 0.2; // パンニングを控えめに const layerVolume = baseVolume * [1.0, 0.5, 0.3][layerIndex]; gain.gain.setValueAtTime(0, currentTime); gain.gain.linearRampToValueAtTime(layerVolume, currentTime + 0.08); gain.gain.exponentialRampToValueAtTime(0.001, currentTime + duration); osc.connect(filter); filter.connect(gain); gain.connect(panner); panner.connect(this.reverb); osc.start(currentTime); osc.stop(currentTime + duration + 0.1); }); }, i * 150); // 少し間隔を広げて余裕を持たせる }); } // ズーム状態による音響環境調整 updateZoomAmbience(cameraZ) { if (!this.isInitialized || this.isMuted) return; this.currentZoomLevel = cameraZ; const currentTime = this.audioContext.currentTime; const zoomRatio = (cameraZ - 200) / (2000 - 200); if (this.zoomEQ) { if (zoomRatio < 0.3) { this.zoomEQ.frequency.setTargetAtTime(2500, currentTime, 0.8); this.zoomEQ.gain.setTargetAtTime(2, currentTime, 0.8); } else if (zoomRatio > 0.7) { this.zoomEQ.frequency.setTargetAtTime(300, currentTime, 0.8); this.zoomEQ.gain.setTargetAtTime(3, currentTime, 0.8); } else { this.zoomEQ.frequency.setTargetAtTime(1000, currentTime, 0.8); this.zoomEQ.gain.setTargetAtTime(0.5, currentTime, 0.8); } } } // メロディー音符を再生(より心地よく) playMelodicNote(frequency, volume = 0.1, duration = 1.8, influence = 0) { if (!this.isInitialized || this.isMuted) return; const layers = [ { type: 'sine', detune: 0, vol: 1.0, pan: 0 }, { type: 'triangle', detune: 12, vol: 0.4, pan: -0.15 }, { type: 'sine', detune: 19, vol: 0.25, pan: 0.15 } ]; layers.forEach((layer, index) => { setTimeout(() => { const osc = this.audioContext.createOscillator(); const gain = this.audioContext.createGain(); const filter = this.audioContext.createBiquadFilter(); const panner = this.audioContext.createStereoPanner(); osc.type = layer.type; osc.frequency.setValueAtTime(frequency, this.audioContext.currentTime); osc.detune.setValueAtTime(layer.detune, this.audioContext.currentTime); filter.type = 'lowpass'; const filterFreq = frequency * (2.5 + influence * 1.5); // より控えめなフィルター filter.frequency.setValueAtTime(filterFreq, this.audioContext.currentTime); filter.Q.setValueAtTime(0.8 + influence * 2, this.audioContext.currentTime); panner.pan.value = layer.pan + (Math.random() - 0.5) * 0.1; const layerVolume = volume * layer.vol * (0.7 + influence * 0.2); gain.gain.setValueAtTime(0, this.audioContext.currentTime); gain.gain.linearRampToValueAtTime(layerVolume, this.audioContext.currentTime + 0.12); gain.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration); osc.connect(filter); filter.connect(gain); gain.connect(panner); panner.connect(this.reverb); osc.start(); osc.stop(this.audioContext.currentTime + duration); this.activeNodes.oscillators.push(osc); this.activeNodes.gains.push(gain); this.activeNodes.filters.push(filter); }, index * 40); }); } // 環境音パッド(より心地よく) createAmbientPad(frequency, volume = 0.03, fadeIn = 8) { if (!this.isInitialized || this.isMuted) return; const osc = this.audioContext.createOscillator(); const gain = this.audioContext.createGain(); const filter = this.audioContext.createBiquadFilter(); const panner = this.audioContext.createStereoPanner(); osc.type = 'sine'; osc.frequency.setValueAtTime(frequency, this.audioContext.currentTime); filter.type = 'lowpass'; filter.frequency.setValueAtTime(frequency * 2.5, this.audioContext.currentTime); // より柔らかく filter.Q.setValueAtTime(0.3, this.audioContext.currentTime); // より自然に panner.pan.value = (Math.random() - 0.5) * 0.4; // パンニング控えめ const lfo = this.audioContext.createOscillator(); const lfoGain = this.audioContext.createGain(); lfo.type = 'sine'; lfo.frequency.value = 0.08 + Math.random() * 0.08; // よりゆっくりとした変化 lfoGain.gain.value = frequency * 0.002; lfo.connect(lfoGain); lfoGain.connect(osc.frequency); gain.gain.setValueAtTime(0, this.audioContext.currentTime); gain.gain.linearRampToValueAtTime(volume, this.audioContext.currentTime + fadeIn); osc.connect(filter); filter.connect(gain); gain.connect(panner); panner.connect(this.reverb); osc.start(); lfo.start(); this.activeNodes.oscillators.push(osc, lfo); this.activeNodes.gains.push(gain, lfoGain); this.activeNodes.filters.push(filter); return { osc, lfo, gain }; } // リアルタイムエフェクト(より心地よく) createRealtimeEffect(frequency, influence) { if (!this.isInitialized || this.isMuted || influence < 0.25) return; const osc = this.audioContext.createOscillator(); const gain = this.audioContext.createGain(); const filter = this.audioContext.createBiquadFilter(); const panner = this.audioContext.createStereoPanner(); osc.type = 'sine'; osc.frequency.setValueAtTime(frequency * (1 + influence * 0.2), this.audioContext.currentTime); filter.type = 'lowpass'; filter.frequency.setValueAtTime(frequency * (1.8 + influence * 1.2), this.audioContext.currentTime); filter.Q.setValueAtTime(influence * 2.5, this.audioContext.currentTime); panner.pan.value = (Math.random() - 0.5) * 0.3; const volume = influence * 0.1; // より控えめな音量 const duration = 0.3 + influence * 0.5; gain.gain.setValueAtTime(0, this.audioContext.currentTime); gain.gain.linearRampToValueAtTime(volume, this.audioContext.currentTime + 0.08); gain.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration); osc.connect(filter); filter.connect(gain); gain.connect(panner); panner.connect(this.reverb); osc.start(); osc.stop(this.audioContext.currentTime + duration); } // インタラクティブサウンド更新 updateSoundFromInteraction(influence, rotationSpeed) { if (!this.isInitialized || this.isMuted) return; const currentTime = this.audioContext.currentTime; this.activeNodes.filters.forEach(filter => { if (filter && filter.frequency) { const targetFreq = this.soundParams.baseFrequency * (1.8 + influence * 2); filter.frequency.setTargetAtTime(targetFreq, currentTime, 0.15); if (filter.Q) { const targetQ = 0.8 + influence * 2.5; filter.Q.setTargetAtTime(targetQ, currentTime, 0.15); } } }); if (influence > 0.25) { if (Math.random() < 0.06 * influence) { const pattern = this.melodyPatterns[this.currentPattern]; const noteIndex = pattern[Math.floor(Math.random() * pattern.length)]; const safeIndex = noteIndex % this.currentScale.length; const frequency = this.currentScale[safeIndex]; const volume = 0.08 + influence * 0.12; const duration = 1.2 + influence * 0.6; this.playMelodicNote(frequency, volume, duration, influence); } } if (rotationSpeed > 0.008) { if (Math.random() < rotationSpeed * 0.2) { const randomNote = Math.floor(Math.random() * this.currentScale.length); const frequency = this.currentScale[randomNote]; this.playMelodicNote(frequency, 0.06, 1.0, influence); } } if (this.masterGain) { const targetVolume = 0.4 + influence * 0.15; this.masterGain.gain.setTargetAtTime(targetVolume, currentTime, 0.3); } } // メロディーシーケンス startMelodySequence() { if (this.melodyInterval) { clearInterval(this.melodyInterval); } // テーマ別パターン選択 const themePatterns = { 1: 'gentle', // Blue 2: 'peaceful', // White 3: 'warm', // Red 4: 'flowing', // Gold 5: 'dreamy' // Rainbow }; this.currentPattern = themePatterns[COSMIC_PIECE] || 'gentle'; const beatInterval = (60 / this.soundParams.tempo) * 1000 * 4; // より長い間隔 this.melodyInterval = setInterval(() => { if (!this.isMuted) { const pattern = this.melodyPatterns[this.currentPattern]; const noteIndex = pattern[this.patternIndex % pattern.length]; const safeIndex = noteIndex % this.currentScale.length; const frequency = this.currentScale[safeIndex]; if (Math.random() > 0.3) { // 少し頻度を下げる const isAccented = this.patternIndex % 8 === 0; const volume = isAccented ? 0.1 : 0.06; const duration = isAccented ? 2.2 : 1.8; this.playMelodicNote(frequency, volume, duration, 0); } this.patternIndex++; if (this.patternIndex % 16 === 0) { // 時々パターンを変更 if (Math.random() > 0.7) { const patterns = Object.keys(this.melodyPatterns); this.currentPattern = patterns[Math.floor(Math.random() * patterns.length)]; } } } }, beatInterval); } // アンビエント背景音 startAmbientBackground() { if (this.ambientInterval) { clearInterval(this.ambientInterval); } this.ambientInterval = setInterval(() => { if (!this.isMuted && Math.random() > 0.5) { const baseFreq = this.soundParams.baseFrequency / 2; const harmonics = [1, 1.2, 1.5]; const harmonic = harmonics[Math.floor(Math.random() * harmonics.length)]; this.createAmbientPad(baseFreq * harmonic, 0.025, 10); } }, 18000); // より長い間隔 } // コード効果 createChordEffect() { if (!this.isInitialized || this.isMuted) return; const chordIntervals = this.chordTypes[this.soundParams.chordType]; const baseFreq = this.soundParams.baseFrequency / 1.5; chordIntervals.forEach((ratio, index) => { const freq = baseFreq * ratio; setTimeout(() => { this.createAmbientPad(freq, 0.03 / (index + 1), 8); }, index * 1200); }); } // 音楽開始 async startMusic() { if (!this.isInitialized) { await this.initialize(); } this.isMuted = false; this.updateCurrentScale(); this.createChordEffect(); this.startMelodySequence(); this.startAmbientBackground(); console.log(`${CURRENT_THEME.name} music started (Harmonious Edition)`); } // 音楽停止 stopMusic() { this.isMuted = true; if (this.melodyInterval) { clearInterval(this.melodyInterval); this.melodyInterval = null; } if (this.ambientInterval) { clearInterval(this.ambientInterval); this.ambientInterval = null; } this.cleanupAudioNodes(); console.log(`${CURRENT_THEME.name} music stopped`); } cleanupAudioNodes() { const currentTime = this.audioContext ? this.audioContext.currentTime : 0; this.activeNodes.gains.forEach(gain => { if (gain && gain.gain) { try { gain.gain.cancelScheduledValues(currentTime); gain.gain.setValueAtTime(gain.gain.value, currentTime); gain.gain.exponentialRampToValueAtTime(0.001, currentTime + 3); } catch (e) {} } }); setTimeout(() => { this.activeNodes.oscillators.forEach(osc => { if (osc) { try { osc.stop(); } catch (e) {} } }); this.activeNodes = { oscillators: [], gains: [], filters: [] }; }, 3500); } async stop() { this.stopMusic(); if (this.audioContext) { try { await this.audioContext.close(); this.audioContext = null; this.isInitialized = false; } catch (e) { console.error("Error stopping audio context:", e); } } } } let camera, scene, renderer; let particleGroup, particles, particleMaterial; let mouse = new THREE.Vector2(-100, -100); let customCursor; // タッチ・ドラッグ操作用の変数 let isMouseDown = false; let previousMouse = { x: 0, y: 0 }; let touches = []; let lastPinchDistance = null; // Cosmic音楽エンジン let musicEngine = new CosmicPhoenixMusicEngine(); const PARTICLE_COUNT = 8000; const particlesData = []; // レインボーモード用の時間変数 let rainbowTime = 0; function generateSprite() { const canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 128; const context = canvas.getContext('2d'); const gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2); gradient.addColorStop(0, 'rgba(255,255,255,1)'); gradient.addColorStop(0.2, 'rgba(255,255,255,0.8)'); gradient.addColorStop(0.4, 'rgba(200,200,255,0.3)'); gradient.addColorStop(1, 'rgba(0,0,0,0)'); context.fillStyle = gradient; context.fillRect(0, 0, canvas.width, canvas.height); return new THREE.CanvasTexture(canvas); } function getParticleColor(particleIndex, time) { const color = new THREE.Color(); if (CURRENT_THEME.colorMode === "rainbow") { // レインボーモード:時間と位置に基づいて色相を変化 const baseHue = (time * 0.1 + particleIndex * 0.01) % 1; color.setHSL(baseHue, CURRENT_THEME.saturation, CURRENT_THEME.lightness); } else { // 通常モード:テーマの色範囲内でランダム const hueRange = CURRENT_THEME.hueRange; let hue; if (CURRENT_THEME.saturation === 0) { // Gray: saturation 0 hue = 0; // 色相は無関係 } else if (CURRENT_THEME.saturation === 0.1) { // White: low saturation, any hue hue = Math.random(); } else if (hueRange[0] > hueRange[1]) { // Red spectrum (wrapping around hue wheel) const rand = Math.random(); hue = rand < 0.5 ? hueRange[0] + (1 - hueRange[0]) * rand * 2 : hueRange[1] * (rand - 0.5) * 2; } else { // Normal range hue = Math.random() * (hueRange[1] - hueRange[0]) + hueRange[0]; } color.setHSL(hue, CURRENT_THEME.saturation, CURRENT_THEME.lightness); } return color; } function init() { camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000); camera.position.z = 1000; scene = new THREE.Scene(); particleGroup = new THREE.Group(); scene.add(particleGroup); const positions = new Float32Array(PARTICLE_COUNT * 3); const colors = new Float32Array(PARTICLE_COUNT * 3); const n = 1200, n2 = n / 2; for (let i = 0; i < PARTICLE_COUNT; i++) { const x = Math.random() * n - n2; const y = Math.random() * n - n2; const z = Math.random() * n - n2; positions[i * 3] = x; positions[i * 3 + 1] = y; positions[i * 3 + 2] = z; // テーマに基づく色設定 const color = getParticleColor(i, 0); colors[i * 3] = color.r; colors[i * 3 + 1] = color.g; colors[i * 3 + 2] = color.b; particlesData.push({ velocity: new THREE.Vector3(-1 + Math.random() * 2, -1 + Math.random() * 2, -1 + Math.random() * 2).multiplyScalar(0.5), originalPos: new THREE.Vector3(x, y, z), }); } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); particleMaterial = new THREE.PointsMaterial({ size: 20, map: generateSprite(), vertexColors: true, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false }); particles = new THREE.Points(geometry, particleMaterial); particleGroup.add(particles); const cursorGeometry = new THREE.RingGeometry(1.5, 1.7, 32); const cursorMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide, transparent: true, opacity: 0.5 }); customCursor = new THREE.Mesh(cursorGeometry, cursorMaterial); scene.add(customCursor); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // イベントリスナーの追加 window.addEventListener('resize', onWindowResize); // マウスイベント document.addEventListener('mousemove', onMouseMove); document.addEventListener('mousedown', onMouseDown); document.addEventListener('mouseup', onMouseUp); document.addEventListener('wheel', onMouseWheel); // タッチイベント document.addEventListener('touchstart', onTouchStart, { passive: false }); document.addEventListener('touchmove', onTouchMove, { passive: false }); document.addEventListener('touchend', onTouchEnd, { passive: false }); // サウンドコントロール setupSoundControls(); console.log(`${CURRENT_THEME.name} initialized (Harmonious Edition)`); } function setupSoundControls() { const soundInfo = document.getElementById('sound-info'); soundInfo.addEventListener('click', toggleSound); soundInfo.addEventListener('touchstart', function(e) { e.preventDefault(); e.stopPropagation(); }, { passive: false }); soundInfo.addEventListener('touchend', function(e) { e.preventDefault(); e.stopPropagation(); setTimeout(() => { toggleSound(); }, 50); }, { passive: false }); } async function toggleSound() { const soundInfo = document.getElementById('sound-info'); const soundOverlay = document.getElementById('sound-overlay'); // タッチフィードバック if ('ontouchstart' in window) { soundOverlay.classList.add('touched'); setTimeout(() => { soundOverlay.classList.remove('touched'); }, 300); setTimeout(() => { soundOverlay.classList.add('fade-out'); setTimeout(() => { soundOverlay.classList.remove('fade-out'); }, 1000); }, 2000); } if (musicEngine.isMuted) { await musicEngine.startMusic(); soundInfo.classList.remove('sound-off'); soundInfo.classList.add('sound-on'); soundInfo.textContent = 'Sound: ON'; setTimeout(() => { soundInfo.classList.add('compact'); }, 1000); } else { musicEngine.stopMusic(); soundInfo.classList.remove('sound-on', 'compact'); soundInfo.classList.add('sound-off'); soundInfo.textContent = 'Sound: OFF'; } } function animate() { requestAnimationFrame(animate); const time = Date.now() * 0.00005; rainbowTime += 0.016; // ~60fps if (!isMouseDown) { particleGroup.rotation.y += 0.0005; particleGroup.rotation.x += 0.0002; } const positions = particles.geometry.attributes.position.array; const colors = particles.geometry.attributes.color.array; let influence = 0; for (let i = 0; i < PARTICLE_COUNT; i++) { const i3 = i * 3; const data = particlesData[i]; const particlePos = new THREE.Vector3(positions[i3], positions[i3 + 1], positions[i3 + 2]); const targetPos = data.originalPos.clone(); targetPos.x += Math.sin(time * 0.5 + i) * 15; targetPos.y += Math.cos(time * 0.4 + i) * 15; targetPos.z += Math.sin(time * 0.3 + i) * 15; const localMouse = particleGroup.worldToLocal(new THREE.Vector3(mouse.x, mouse.y, 0)); const mouseDistance = particlePos.distanceTo(localMouse); let force = new THREE.Vector3(); if (mouseDistance < 150) { const repulsion = 1 - (mouseDistance / 150); force = particlePos.clone().sub(localMouse).normalize().multiplyScalar(repul
    https://whatsonchain.com/tx/a76da7d2e4e5b1ddc710ea04f38e3c4e2e5d95087cfe6d5aa6509a40176d53fb
    Partial data displayed. To get full data click on Download.