#0
0.00000001 BSV
v© £üaÍïêjj&@Ú:ø'À_Ϭ cordQ text/html M/Ý<!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.
Partial data displayed. To get full data click on Download.
Partial data displayed. To get full data click on Download.