Spaces:
Running
Running
Upload 17 files
Browse files- 7BGbbW7K0gEtscV9M.html +300 -0
- S17esH2eMfe0yEdz6.html +368 -0
- SWis8QTUYIKGMFRWI.html +294 -0
- UZNY2tEsXhS8RqUM9.html +262 -0
- ZEqZXcZpfUDkV8V2M.html +248 -0
- cYUneFx9tfHZWLOXx.html +188 -0
- hbmKkWX8sB2tRrYlW.html +399 -0
- jDnGYMx9M566RB31a.html +300 -0
- mIvBgUJS6Lekllndg.html +402 -0
- nbF9hbyzb3g7zW60m.html +348 -0
- r8IennNoYrHc8Bg9I.html +203 -0
- uoPvvPxhIetjWfFYJ.html +256 -0
- wBMqnCWym2CMFKF1H.html +311 -0
7BGbbW7K0gEtscV9M.html
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://piano.midi/webmidi@latest/flex-horizontal/use-event.note.name+event.note.octave__not.value/in_getKeys_just_do_straight_comparison/whiteKeys_and_blackKeys_are_nodeLists_not_arrays/position_fix_keyboard_above_text/playable-with-mouse-too/hide%3Ccomments%3Eand%3Clinks%3E/drop_piano_2_octaves/abnormify_sound/push_audio_creativity/explore_tonejs_instruments/drop_piano_decibels_by_10/add_higher_octave/hush_comments"><title>Sonic Artistry</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
margin: 0;
|
| 5 |
+
padding: 0;
|
| 6 |
+
background: radial-gradient(circle at center, #111, #333);
|
| 7 |
+
color: #fff;
|
| 8 |
+
font-family: monospace;
|
| 9 |
+
overflow: hidden;
|
| 10 |
+
display: flex;
|
| 11 |
+
flex-direction: column;
|
| 12 |
+
justify-content: center;
|
| 13 |
+
align-items: center;
|
| 14 |
+
height: 100vh;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
#instrumentSelector {
|
| 18 |
+
position: absolute;
|
| 19 |
+
top: 10px;
|
| 20 |
+
left: 10px;
|
| 21 |
+
background-color: rgba(255, 255, 255, 0.1);
|
| 22 |
+
padding: 10px;
|
| 23 |
+
border-radius: 5px;
|
| 24 |
+
z-index: 1;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
#keyboardContainer {
|
| 28 |
+
display: flex;
|
| 29 |
+
flex-direction: row;
|
| 30 |
+
align-items: flex-end;
|
| 31 |
+
margin-bottom: 20px;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.key {
|
| 35 |
+
width: 50px;
|
| 36 |
+
height: 200px;
|
| 37 |
+
background: linear-gradient(to bottom, #eee, #ccc);
|
| 38 |
+
border: 1px solid #000;
|
| 39 |
+
box-sizing: border-box;
|
| 40 |
+
cursor: pointer;
|
| 41 |
+
transition: background-color 0.2s, transform 0.2s;
|
| 42 |
+
position: relative;
|
| 43 |
+
transform-style: preserve-3d;
|
| 44 |
+
display: flex;
|
| 45 |
+
justify-content: center;
|
| 46 |
+
align-items: flex-end;
|
| 47 |
+
padding-bottom: 10px;
|
| 48 |
+
font-size: 12px;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.key::after {
|
| 52 |
+
content: attr(data-note);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.key.black {
|
| 56 |
+
width: 30px;
|
| 57 |
+
height: 120px;
|
| 58 |
+
background: linear-gradient(to bottom, #555, #222);
|
| 59 |
+
margin-right: -15px;
|
| 60 |
+
margin-left: -15px;
|
| 61 |
+
z-index: 1;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.key:hover {
|
| 65 |
+
background: linear-gradient(to bottom, #ddd, #bbb);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
.key.black:hover {
|
| 69 |
+
background: linear-gradient(to bottom, #777, #444);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.key.pressed {
|
| 73 |
+
background: linear-gradient(to bottom, #ccc, #aaa);
|
| 74 |
+
transform: translateZ(20px);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.key.black.pressed {
|
| 78 |
+
background: linear-gradient(to bottom, #666, #333);
|
| 79 |
+
transform: translateZ(20px);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.key::before {
|
| 83 |
+
content: "";
|
| 84 |
+
position: absolute;
|
| 85 |
+
top: 50%;
|
| 86 |
+
left: 50%;
|
| 87 |
+
transform: translate(-50%, -50%);
|
| 88 |
+
width: 0;
|
| 89 |
+
height: 0;
|
| 90 |
+
background-color: rgba(255, 255, 255, 0.5);
|
| 91 |
+
border-radius: 50%;
|
| 92 |
+
opacity: 0;
|
| 93 |
+
transition: width 0.2s, height 0.2s, opacity 0.2s;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.key.pressed::before {
|
| 97 |
+
width: 100px;
|
| 98 |
+
height: 100px;
|
| 99 |
+
opacity: 1;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.key.black::before {
|
| 103 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 104 |
+
}
|
| 105 |
+
</style>
|
| 106 |
+
</head>
|
| 107 |
+
<body>
|
| 108 |
+
<div id="instrumentSelector">
|
| 109 |
+
<label for="instrumentSelect">Select Instrument:</label>
|
| 110 |
+
<select id="instrumentSelect">
|
| 111 |
+
<option value="AMSynth">AM Synth</option>
|
| 112 |
+
<option value="FMSynth">FM Synth</option>
|
| 113 |
+
<option value="DuoSynth">Duo Synth</option>
|
| 114 |
+
<option value="MonoSynth">Mono Synth</option>
|
| 115 |
+
<option value="PluckSynth">Pluck Synth</option>
|
| 116 |
+
<option value="Membrane">Membrane Synth</option>
|
| 117 |
+
<option value="MetalSynth">Metal Synth</option>
|
| 118 |
+
<option value="NoiseSynth">Noise Synth</option>
|
| 119 |
+
</select>
|
| 120 |
+
</div>
|
| 121 |
+
<div id="keyboardContainer">
|
| 122 |
+
<div class="key white" data-note="C2" data-midi="36"></div>
|
| 123 |
+
<div class="key black" data-note="C#2" data-midi="37"></div>
|
| 124 |
+
<div class="key white" data-note="D2" data-midi="38"></div>
|
| 125 |
+
<div class="key black" data-note="D#2" data-midi="39"></div>
|
| 126 |
+
<div class="key white" data-note="E2" data-midi="40"></div>
|
| 127 |
+
<div class="key white" data-note="F2" data-midi="41"></div>
|
| 128 |
+
<div class="key black" data-note="F#2" data-midi="42"></div>
|
| 129 |
+
<div class="key white" data-note="G2" data-midi="43"></div>
|
| 130 |
+
<div class="key black" data-note="G#2" data-midi="44"></div>
|
| 131 |
+
<div class="key white" data-note="A2" data-midi="45"></div>
|
| 132 |
+
<div class="key black" data-note="A#2" data-midi="46"></div>
|
| 133 |
+
<div class="key white" data-note="B2" data-midi="47"></div>
|
| 134 |
+
<div class="key white" data-note="C3" data-midi="48"></div>
|
| 135 |
+
<div class="key black" data-note="C#3" data-midi="49"></div>
|
| 136 |
+
<div class="key white" data-note="D3" data-midi="50"></div>
|
| 137 |
+
<div class="key black" data-note="D#3" data-midi="51"></div>
|
| 138 |
+
<div class="key white" data-note="E3" data-midi="52"></div>
|
| 139 |
+
<div class="key white" data-note="F3" data-midi="53"></div>
|
| 140 |
+
<div class="key black" data-note="F#3" data-midi="54"></div>
|
| 141 |
+
<div class="key white" data-note="G3" data-midi="55"></div>
|
| 142 |
+
<div class="key black" data-note="G#3" data-midi="56"></div>
|
| 143 |
+
<div class="key white" data-note="A3" data-midi="57"></div>
|
| 144 |
+
<div class="key black" data-note="A#3" data-midi="58"></div>
|
| 145 |
+
<div class="key white" data-note="B3" data-midi="59"></div>
|
| 146 |
+
<div class="key white" data-note="C4" data-midi="60"></div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<script src="https://cdn.jsdelivr.net/npm/tone@14.7.77/build/Tone.js"></script>
|
| 150 |
+
<script src="https://cdn.jsdelivr.net/npm/webmidi@latest/dist/iife/webmidi.iife.min.js"></script>
|
| 151 |
+
<script>
|
| 152 |
+
const whiteKeys = document.querySelectorAll('.key.white');
|
| 153 |
+
const blackKeys = document.querySelectorAll('.key.black');
|
| 154 |
+
const instrumentSelect = document.getElementById('instrumentSelect');
|
| 155 |
+
let synth;
|
| 156 |
+
const reverb = new Tone.Reverb({ decay: 5, wet: 0.5 }).toDestination();
|
| 157 |
+
const delay = new Tone.FeedbackDelay({ delayTime: 0.5, feedback: 0.25, wet: 0.25 }).toDestination();
|
| 158 |
+
|
| 159 |
+
function initSynth(instrumentType) {
|
| 160 |
+
if (synth) {
|
| 161 |
+
synth.disconnect();
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
switch (instrumentType) {
|
| 165 |
+
case 'AMSynth':
|
| 166 |
+
synth = new Tone.PolySynth(Tone.AMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 167 |
+
break;
|
| 168 |
+
case 'FMSynth':
|
| 169 |
+
synth = new Tone.PolySynth(Tone.FMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 170 |
+
break;
|
| 171 |
+
case 'DuoSynth':
|
| 172 |
+
synth = new Tone.PolySynth(Tone.DuoSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 173 |
+
break;
|
| 174 |
+
case 'MonoSynth':
|
| 175 |
+
synth = new Tone.MonoSynth({ volume: -10 }).toDestination();
|
| 176 |
+
break;
|
| 177 |
+
case 'PluckSynth':
|
| 178 |
+
synth = new Tone.PluckSynth({ volume: -10 }).toDestination();
|
| 179 |
+
break;
|
| 180 |
+
case 'Membrane':
|
| 181 |
+
synth = new Tone.PolySynth(Tone.MembraneSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 182 |
+
break;
|
| 183 |
+
case 'MetalSynth':
|
| 184 |
+
synth = new Tone.PolySynth(Tone.MetalSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 185 |
+
break;
|
| 186 |
+
case 'NoiseSynth':
|
| 187 |
+
synth = new Tone.NoiseSynth({ volume: -10 }).toDestination();
|
| 188 |
+
break;
|
| 189 |
+
default:
|
| 190 |
+
synth = new Tone.PolySynth(Tone.AMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
synth.connect(reverb);
|
| 194 |
+
synth.connect(delay);
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
initSynth(instrumentSelect.value);
|
| 198 |
+
|
| 199 |
+
instrumentSelect.addEventListener('change', (event) => {
|
| 200 |
+
initSynth(event.target.value);
|
| 201 |
+
});
|
| 202 |
+
|
| 203 |
+
let midiAccess, midiInput;
|
| 204 |
+
|
| 205 |
+
WebMidi.enable(err => {
|
| 206 |
+
if (err) {
|
| 207 |
+
console.error('Failed to enable WebMIDI:', err);
|
| 208 |
+
return;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
console.log('WebMIDI enabled!');
|
| 212 |
+
midiInput = WebMidi.inputs[0];
|
| 213 |
+
|
| 214 |
+
if (!midiInput) {
|
| 215 |
+
console.warn('No MIDI input device detected.');
|
| 216 |
+
return;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
console.log(`Connected to MIDI input device: ${midiInput.name}`);
|
| 220 |
+
|
| 221 |
+
midiInput.addListener('noteon', 'all', event => {
|
| 222 |
+
const midiNoteValue = `${event.note.name}${event.note.octave}`;
|
| 223 |
+
handleNoteOn(midiNoteValue, event.velocity / 127);
|
| 224 |
+
});
|
| 225 |
+
|
| 226 |
+
midiInput.addListener('noteoff', 'all', event => {
|
| 227 |
+
const midiNoteValue = `${event.note.name}${event.note.octave}`;
|
| 228 |
+
handleNoteOff(midiNoteValue);
|
| 229 |
+
});
|
| 230 |
+
});
|
| 231 |
+
|
| 232 |
+
function handleNoteOn(midiNoteValue, velocity) {
|
| 233 |
+
const whiteKey = Array.from(whiteKeys).find(key => key.dataset.note === midiNoteValue);
|
| 234 |
+
const blackKey = Array.from(blackKeys).find(key => key.dataset.note === midiNoteValue);
|
| 235 |
+
|
| 236 |
+
if (whiteKey) {
|
| 237 |
+
whiteKey.classList.add('pressed');
|
| 238 |
+
synth.triggerAttack(noteToFrequency(whiteKey.dataset.midi), velocity);
|
| 239 |
+
} else if (blackKey) {
|
| 240 |
+
blackKey.classList.add('pressed');
|
| 241 |
+
synth.triggerAttack(noteToFrequency(blackKey.dataset.midi), velocity);
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
function handleNoteOff(midiNoteValue) {
|
| 246 |
+
const whiteKey = Array.from(whiteKeys).find(key => key.dataset.note === midiNoteValue);
|
| 247 |
+
const blackKey = Array.from(blackKeys).find(key => key.dataset.note === midiNoteValue);
|
| 248 |
+
|
| 249 |
+
if (whiteKey) {
|
| 250 |
+
whiteKey.classList.remove('pressed');
|
| 251 |
+
synth.triggerRelease(noteToFrequency(whiteKey.dataset.midi));
|
| 252 |
+
} else if (blackKey) {
|
| 253 |
+
blackKey.classList.remove('pressed');
|
| 254 |
+
synth.triggerRelease(noteToFrequency(blackKey.dataset.midi));
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
function whiteKeyMouseDown(event) {
|
| 259 |
+
const key = event.target;
|
| 260 |
+
key.classList.add('pressed');
|
| 261 |
+
synth.triggerAttack(noteToFrequency(key.dataset.midi), 0.5);
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
function whiteKeyMouseUp(event) {
|
| 265 |
+
const key = event.target;
|
| 266 |
+
key.classList.remove('pressed');
|
| 267 |
+
synth.triggerRelease(noteToFrequency(key.dataset.midi));
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
function blackKeyMouseDown(event) {
|
| 271 |
+
const key = event.target;
|
| 272 |
+
key.classList.add('pressed');
|
| 273 |
+
synth.triggerAttack(noteToFrequency(key.dataset.midi), 0.5);
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
function blackKeyMouseUp(event) {
|
| 277 |
+
const key = event.target;
|
| 278 |
+
key.classList.remove('pressed');
|
| 279 |
+
synth.triggerRelease(noteToFrequency(key.dataset.midi));
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
whiteKeys.forEach(key => {
|
| 283 |
+
key.addEventListener('mousedown', whiteKeyMouseDown);
|
| 284 |
+
key.addEventListener('mouseup', whiteKeyMouseUp);
|
| 285 |
+
key.addEventListener('mouseleave', whiteKeyMouseUp);
|
| 286 |
+
});
|
| 287 |
+
|
| 288 |
+
blackKeys.forEach(key => {
|
| 289 |
+
key.addEventListener('mousedown', blackKeyMouseDown);
|
| 290 |
+
key.addEventListener('mouseup', blackKeyMouseUp);
|
| 291 |
+
key.addEventListener('mouseleave', blackKeyMouseUp);
|
| 292 |
+
});
|
| 293 |
+
|
| 294 |
+
function noteToFrequency(note) {
|
| 295 |
+
const baseFrequency = 440;
|
| 296 |
+
return baseFrequency * Math.pow(2, (note - 69) / 12);
|
| 297 |
+
}
|
| 298 |
+
</script>
|
| 299 |
+
</body>
|
| 300 |
+
</html>
|
S17esH2eMfe0yEdz6.html
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="Simulate://Earth.ai/3D_Graphs=enabled/WebGL=enabled/Physics=enabled/Weather.gif=enabled/Weather.gif=visible/Seasons.gif=enabled/City_Lights=enabled/City_Lights=visible/Stars=Visible/Sun=visible"><title>Earth.ai - Interactive Earth and Sun Simulation</title>
|
| 2 |
+
<style>
|
| 3 |
+
body, html {
|
| 4 |
+
margin: 0;
|
| 5 |
+
padding: 0;
|
| 6 |
+
height: 100%;
|
| 7 |
+
font-family: Arial, sans-serif;
|
| 8 |
+
overflow: hidden;
|
| 9 |
+
background-color: #000;
|
| 10 |
+
color: #fff;
|
| 11 |
+
}
|
| 12 |
+
#canvas-container {
|
| 13 |
+
position: absolute;
|
| 14 |
+
width: 100%;
|
| 15 |
+
height: 100%;
|
| 16 |
+
}
|
| 17 |
+
#overlay {
|
| 18 |
+
position: absolute;
|
| 19 |
+
top: 10px;
|
| 20 |
+
left: 10px;
|
| 21 |
+
background-color: rgba(0,0,0,0.7);
|
| 22 |
+
padding: 10px;
|
| 23 |
+
border-radius: 5px;
|
| 24 |
+
}
|
| 25 |
+
#controls {
|
| 26 |
+
position: absolute;
|
| 27 |
+
bottom: 10px;
|
| 28 |
+
left: 10px;
|
| 29 |
+
background-color: rgba(0,0,0,0.7);
|
| 30 |
+
padding: 10px;
|
| 31 |
+
border-radius: 5px;
|
| 32 |
+
}
|
| 33 |
+
button {
|
| 34 |
+
background-color: #4CAF50;
|
| 35 |
+
border: none;
|
| 36 |
+
color: white;
|
| 37 |
+
padding: 8px 16px;
|
| 38 |
+
text-align: center;
|
| 39 |
+
text-decoration: none;
|
| 40 |
+
display: inline-block;
|
| 41 |
+
font-size: 12px;
|
| 42 |
+
margin: 4px 2px;
|
| 43 |
+
cursor: pointer;
|
| 44 |
+
border-radius: 5px;
|
| 45 |
+
}
|
| 46 |
+
#info-panel {
|
| 47 |
+
position: absolute;
|
| 48 |
+
top: 10px;
|
| 49 |
+
right: 10px;
|
| 50 |
+
background-color: rgba(0,0,0,0.7);
|
| 51 |
+
padding: 10px;
|
| 52 |
+
border-radius: 5px;
|
| 53 |
+
font-size: 14px;
|
| 54 |
+
}
|
| 55 |
+
#earth-info {
|
| 56 |
+
position: absolute;
|
| 57 |
+
bottom: 10px;
|
| 58 |
+
right: 10px;
|
| 59 |
+
background-color: rgba(0,0,0,0.7);
|
| 60 |
+
padding: 10px;
|
| 61 |
+
border-radius: 5px;
|
| 62 |
+
font-size: 14px;
|
| 63 |
+
}
|
| 64 |
+
</style>
|
| 65 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 66 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
|
| 67 |
+
</head>
|
| 68 |
+
<body>
|
| 69 |
+
<div id="canvas-container"></div>
|
| 70 |
+
<div id="overlay">
|
| 71 |
+
<h1>Earth.ai Interactive Simulation</h1>
|
| 72 |
+
<p>Explore Earth's dynamics with the Sun!</p>
|
| 73 |
+
</div>
|
| 74 |
+
<div id="controls">
|
| 75 |
+
<button id="toggle-atmosphere">Toggle Atmosphere</button>
|
| 76 |
+
<button id="toggle-weather">Toggle Weather</button>
|
| 77 |
+
<button id="toggle-seasons">Toggle Seasons</button>
|
| 78 |
+
<button id="toggle-city-lights">Toggle City Lights</button>
|
| 79 |
+
<button id="toggle-stars">Toggle Stars</button>
|
| 80 |
+
<button id="toggle-sun">Toggle Sun</button>
|
| 81 |
+
<button id="reset-view">Reset View</button>
|
| 82 |
+
</div>
|
| 83 |
+
<div id="info-panel">
|
| 84 |
+
<p>Current Season: <span id="current-season">Spring</span></p>
|
| 85 |
+
<p>Time: <span id="current-time">12:00 PM</span></p>
|
| 86 |
+
<p>Weather: <span id="current-weather">Clear</span></p>
|
| 87 |
+
<p>Temperature: <span id="current-temperature">20°C</span></p>
|
| 88 |
+
</div>
|
| 89 |
+
<div id="earth-info">
|
| 90 |
+
<p>Earth Rotation: <span id="earth-rotation">0°</span></p>
|
| 91 |
+
<p>Day/Night: <span id="day-night">Day</span></p>
|
| 92 |
+
<p>Solar Intensity: <span id="solar-intensity">100%</span></p>
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
<script>
|
| 96 |
+
let scene, camera, renderer, earth, atmosphere, weatherLayer, seasonalLayer, cityLights, stars, sun;
|
| 97 |
+
let currentSeason = 'Spring';
|
| 98 |
+
let currentTime = new Date();
|
| 99 |
+
let weatherConditions = ['Clear', 'Cloudy', 'Rainy', 'Snowy'];
|
| 100 |
+
let currentWeather = 'Clear';
|
| 101 |
+
let currentTemperature = 20;
|
| 102 |
+
let earthRotation = 0;
|
| 103 |
+
let isDaytime = true;
|
| 104 |
+
let solarIntensity = 100;
|
| 105 |
+
|
| 106 |
+
function init() {
|
| 107 |
+
scene = new THREE.Scene();
|
| 108 |
+
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
| 109 |
+
renderer = new THREE.WebGLRenderer({ antialias: true });
|
| 110 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 111 |
+
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
| 112 |
+
|
| 113 |
+
// Create Earth
|
| 114 |
+
const earthGeometry = new THREE.SphereGeometry(5, 64, 64);
|
| 115 |
+
const earthTexture = new THREE.TextureLoader().load('https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Solarsystemscope_texture_2k_earth_daymap.jpg/2560px-Solarsystemscope_texture_2k_earth_daymap.jpg');
|
| 116 |
+
const earthMaterial = new THREE.MeshPhongMaterial({ map: earthTexture });
|
| 117 |
+
earth = new THREE.Mesh(earthGeometry, earthMaterial);
|
| 118 |
+
scene.add(earth);
|
| 119 |
+
|
| 120 |
+
// Create Atmosphere
|
| 121 |
+
const atmosphereGeometry = new THREE.SphereGeometry(5.1, 64, 64);
|
| 122 |
+
const atmosphereMaterial = new THREE.MeshPhongMaterial({
|
| 123 |
+
color: 0x00ffff,
|
| 124 |
+
transparent: true,
|
| 125 |
+
opacity: 0.2
|
| 126 |
+
});
|
| 127 |
+
atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
|
| 128 |
+
scene.add(atmosphere);
|
| 129 |
+
|
| 130 |
+
// Create Weather Layer
|
| 131 |
+
weatherLayer = new THREE.Mesh(
|
| 132 |
+
new THREE.SphereGeometry(5.05, 64, 64),
|
| 133 |
+
new THREE.MeshPhongMaterial({
|
| 134 |
+
transparent: true,
|
| 135 |
+
opacity: 0.8,
|
| 136 |
+
map: new THREE.TextureLoader().load('https://upload.wikimedia.org/wikipedia/commons/thumb/2/25/Solarsystemscope_texture_2k_earth_clouds.jpg/1280px-Solarsystemscope_texture_2k_earth_clouds.jpg')
|
| 137 |
+
})
|
| 138 |
+
);
|
| 139 |
+
scene.add(weatherLayer);
|
| 140 |
+
|
| 141 |
+
// Create Seasonal Layer
|
| 142 |
+
seasonalLayer = new THREE.Mesh(
|
| 143 |
+
new THREE.SphereGeometry(5.02, 64, 64),
|
| 144 |
+
new THREE.MeshPhongMaterial({
|
| 145 |
+
transparent: true,
|
| 146 |
+
opacity: 0.5,
|
| 147 |
+
map: new THREE.TextureLoader().load('https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Solarsystemscope_texture_2k_earth_vegetation.jpg/1280px-Solarsystemscope_texture_2k_earth_vegetation.jpg')
|
| 148 |
+
})
|
| 149 |
+
);
|
| 150 |
+
scene.add(seasonalLayer);
|
| 151 |
+
|
| 152 |
+
// Create City Lights
|
| 153 |
+
cityLights = new THREE.Mesh(
|
| 154 |
+
new THREE.SphereGeometry(5.01, 64, 64),
|
| 155 |
+
new THREE.MeshBasicMaterial({
|
| 156 |
+
transparent: true,
|
| 157 |
+
opacity: 0.8,
|
| 158 |
+
map: new THREE.TextureLoader().load('https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg')
|
| 159 |
+
})
|
| 160 |
+
);
|
| 161 |
+
scene.add(cityLights);
|
| 162 |
+
|
| 163 |
+
// Create Stars
|
| 164 |
+
const starsGeometry = new THREE.BufferGeometry();
|
| 165 |
+
const starsMaterial = new THREE.PointsMaterial({color: 0xFFFFFF, size: 0.05});
|
| 166 |
+
const starsVertices = [];
|
| 167 |
+
for (let i = 0; i < 10000; i++) {
|
| 168 |
+
const x = THREE.MathUtils.randFloatSpread(2000);
|
| 169 |
+
const y = THREE.MathUtils.randFloatSpread(2000);
|
| 170 |
+
const z = THREE.MathUtils.randFloatSpread(2000);
|
| 171 |
+
starsVertices.push(x, y, z);
|
| 172 |
+
}
|
| 173 |
+
starsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starsVertices, 3));
|
| 174 |
+
stars = new THREE.Points(starsGeometry, starsMaterial);
|
| 175 |
+
scene.add(stars);
|
| 176 |
+
|
| 177 |
+
// Create Sun
|
| 178 |
+
const sunGeometry = new THREE.SphereGeometry(20, 64, 64);
|
| 179 |
+
const sunMaterial = new THREE.MeshBasicMaterial({
|
| 180 |
+
color: 0xffff00,
|
| 181 |
+
emissive: 0xffff00,
|
| 182 |
+
emissiveIntensity: 1
|
| 183 |
+
});
|
| 184 |
+
sun = new THREE.Mesh(sunGeometry, sunMaterial);
|
| 185 |
+
sun.position.set(50, 20, -100);
|
| 186 |
+
scene.add(sun);
|
| 187 |
+
|
| 188 |
+
// Add ambient light
|
| 189 |
+
const ambientLight = new THREE.AmbientLight(0x404040);
|
| 190 |
+
scene.add(ambientLight);
|
| 191 |
+
|
| 192 |
+
// Add directional light (sunlight)
|
| 193 |
+
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
|
| 194 |
+
sunLight.position.set(50, 20, -100);
|
| 195 |
+
scene.add(sunLight);
|
| 196 |
+
|
| 197 |
+
camera.position.z = 20;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
function updateWeather() {
|
| 201 |
+
currentWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
|
| 202 |
+
document.getElementById('current-weather').textContent = currentWeather;
|
| 203 |
+
|
| 204 |
+
gsap.to(weatherLayer.material, {
|
| 205 |
+
opacity: currentWeather === 'Clear' ? 0.2 : 0.8,
|
| 206 |
+
duration: 2
|
| 207 |
+
});
|
| 208 |
+
|
| 209 |
+
updateTemperature();
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
function updateSeason() {
|
| 213 |
+
const seasons = ['Spring', 'Summer', 'Autumn', 'Winter'];
|
| 214 |
+
const currentIndex = seasons.indexOf(currentSeason);
|
| 215 |
+
currentSeason = seasons[(currentIndex + 1) % seasons.length];
|
| 216 |
+
document.getElementById('current-season').textContent = currentSeason;
|
| 217 |
+
|
| 218 |
+
const seasonColors = {
|
| 219 |
+
Spring: 0x00ff00,
|
| 220 |
+
Summer: 0x00aa00,
|
| 221 |
+
Autumn: 0xaa5500,
|
| 222 |
+
Winter: 0xffffff
|
| 223 |
+
};
|
| 224 |
+
|
| 225 |
+
gsap.to(seasonalLayer.material.color, {
|
| 226 |
+
r: (seasonColors[currentSeason] >> 16 & 255) / 255,
|
| 227 |
+
g: (seasonColors[currentSeason] >> 8 & 255) / 255,
|
| 228 |
+
b: (seasonColors[currentSeason] & 255) / 255,
|
| 229 |
+
duration: 2
|
| 230 |
+
});
|
| 231 |
+
|
| 232 |
+
updateTemperature();
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
function updateTemperature() {
|
| 236 |
+
const baseTemp = {
|
| 237 |
+
Spring: 15,
|
| 238 |
+
Summer: 25,
|
| 239 |
+
Autumn: 10,
|
| 240 |
+
Winter: 0
|
| 241 |
+
}[currentSeason];
|
| 242 |
+
|
| 243 |
+
const weatherEffect = {
|
| 244 |
+
Clear: 5,
|
| 245 |
+
Cloudy: 0,
|
| 246 |
+
Rainy: -3,
|
| 247 |
+
Snowy: -10
|
| 248 |
+
}[currentWeather];
|
| 249 |
+
|
| 250 |
+
currentTemperature = baseTemp + weatherEffect + Math.floor(Math.random() * 5);
|
| 251 |
+
document.getElementById('current-temperature').textContent = `${currentTemperature}°C`;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
function updateTime() {
|
| 255 |
+
currentTime.setMinutes(currentTime.getMinutes() + 1);
|
| 256 |
+
document.getElementById('current-time').textContent = currentTime.toLocaleTimeString();
|
| 257 |
+
|
| 258 |
+
const hours = currentTime.getHours();
|
| 259 |
+
isDaytime = hours >= 6 && hours < 18;
|
| 260 |
+
document.getElementById('day-night').textContent = isDaytime ? 'Day' : 'Night';
|
| 261 |
+
|
| 262 |
+
gsap.to(cityLights.material, {
|
| 263 |
+
opacity: isDaytime ? 0 : 0.8,
|
| 264 |
+
duration: 1
|
| 265 |
+
});
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
function updateSolarIntensity() {
|
| 269 |
+
const angle = (earthRotation % 360) * (Math.PI / 180);
|
| 270 |
+
solarIntensity = Math.max(0, Math.sin(angle)) * 100;
|
| 271 |
+
document.getElementById('solar-intensity').textContent = `${Math.round(solarIntensity)}%`;
|
| 272 |
+
|
| 273 |
+
// Adjust sun's visible intensity
|
| 274 |
+
sun.material.emissiveIntensity = 0.5 + (solarIntensity / 200);
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
function animate() {
|
| 278 |
+
requestAnimationFrame(animate);
|
| 279 |
+
|
| 280 |
+
earthRotation = (earthRotation + 0.1) % 360;
|
| 281 |
+
earth.rotation.y += 0.001;
|
| 282 |
+
atmosphere.rotation.y += 0.001;
|
| 283 |
+
weatherLayer.rotation.y += 0.0012;
|
| 284 |
+
seasonalLayer.rotation.y += 0.001;
|
| 285 |
+
cityLights.rotation.y += 0.001;
|
| 286 |
+
|
| 287 |
+
updateTime();
|
| 288 |
+
updateSolarIntensity();
|
| 289 |
+
|
| 290 |
+
document.getElementById('earth-rotation').textContent = `${earthRotation.toFixed(1)}°`;
|
| 291 |
+
|
| 292 |
+
renderer.render(scene, camera);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
init();
|
| 296 |
+
animate();
|
| 297 |
+
|
| 298 |
+
setInterval(updateWeather, 30000);
|
| 299 |
+
setInterval(updateSeason, 120000);
|
| 300 |
+
|
| 301 |
+
// Event Listeners
|
| 302 |
+
document.getElementById('toggle-atmosphere').addEventListener('click', () => atmosphere.visible = !atmosphere.visible);
|
| 303 |
+
document.getElementById('toggle-weather').addEventListener('click', () => weatherLayer.visible = !weatherLayer.visible);
|
| 304 |
+
document.getElementById('toggle-seasons').addEventListener('click', () => seasonalLayer.visible = !seasonalLayer.visible);
|
| 305 |
+
document.getElementById('toggle-city-lights').addEventListener('click', () => cityLights.visible = !cityLights.visible);
|
| 306 |
+
document.getElementById('toggle-stars').addEventListener('click', () => stars.visible = !stars.visible);
|
| 307 |
+
document.getElementById('toggle-sun').addEventListener('click', () => {
|
| 308 |
+
sun.visible = !sun.visible;
|
| 309 |
+
scene.children.forEach(child => {
|
| 310 |
+
if (child instanceof THREE.DirectionalLight) {
|
| 311 |
+
child.visible = sun.visible;
|
| 312 |
+
}
|
| 313 |
+
});
|
| 314 |
+
});
|
| 315 |
+
document.getElementById('reset-view').addEventListener('click', () => {
|
| 316 |
+
camera.position.set(0, 0, 20);
|
| 317 |
+
camera.lookAt(earth.position);
|
| 318 |
+
});
|
| 319 |
+
|
| 320 |
+
window.addEventListener('resize', () => {
|
| 321 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
| 322 |
+
camera.updateProjectionMatrix();
|
| 323 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 324 |
+
});
|
| 325 |
+
|
| 326 |
+
// Add mouse control for camera movement
|
| 327 |
+
let isDragging = false;
|
| 328 |
+
let previousMousePosition = {
|
| 329 |
+
x: 0,
|
| 330 |
+
y: 0
|
| 331 |
+
};
|
| 332 |
+
|
| 333 |
+
document.addEventListener('mousedown', (e) => {
|
| 334 |
+
isDragging = true;
|
| 335 |
+
});
|
| 336 |
+
|
| 337 |
+
document.addEventListener('mousemove', (e) => {
|
| 338 |
+
if (isDragging) {
|
| 339 |
+
const deltaMove = {
|
| 340 |
+
x: e.clientX - previousMousePosition.x,
|
| 341 |
+
y: e.clientY - previousMousePosition.y
|
| 342 |
+
};
|
| 343 |
+
|
| 344 |
+
const rotationSpeed = 0.005;
|
| 345 |
+
camera.position.x += deltaMove.x * rotationSpeed;
|
| 346 |
+
camera.position.y -= deltaMove.y * rotationSpeed;
|
| 347 |
+
camera.lookAt(earth.position);
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
previousMousePosition = {
|
| 351 |
+
x: e.clientX,
|
| 352 |
+
y: e.clientY
|
| 353 |
+
};
|
| 354 |
+
});
|
| 355 |
+
|
| 356 |
+
document.addEventListener('mouseup', (e) => {
|
| 357 |
+
isDragging = false;
|
| 358 |
+
});
|
| 359 |
+
|
| 360 |
+
// Add zoom functionality
|
| 361 |
+
document.addEventListener('wheel', (e) => {
|
| 362 |
+
const zoomSpeed = 0.1;
|
| 363 |
+
camera.position.z += e.deltaY * zoomSpeed;
|
| 364 |
+
camera.position.z = Math.max(10, Math.min(camera.position.z, 50));
|
| 365 |
+
});
|
| 366 |
+
</script>
|
| 367 |
+
|
| 368 |
+
</body></html>
|
SWis8QTUYIKGMFRWI.html
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://websim.ai/comic-maker"><title>Dynamic Strip Comic Maker</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
font-family: Arial, sans-serif;
|
| 5 |
+
max-width: 1200px;
|
| 6 |
+
margin: 0 auto;
|
| 7 |
+
padding: 20px;
|
| 8 |
+
background-color: #f0f0f0;
|
| 9 |
+
}
|
| 10 |
+
h1 {
|
| 11 |
+
text-align: center;
|
| 12 |
+
color: #333;
|
| 13 |
+
}
|
| 14 |
+
.top-container {
|
| 15 |
+
display: flex;
|
| 16 |
+
justify-content: space-between;
|
| 17 |
+
align-items: flex-start;
|
| 18 |
+
margin-bottom: 20px;
|
| 19 |
+
}
|
| 20 |
+
.instructions {
|
| 21 |
+
flex-grow: 1;
|
| 22 |
+
margin-left: 20px;
|
| 23 |
+
font-size: 0.85em;
|
| 24 |
+
line-height: 1.4;
|
| 25 |
+
background-color: #fff;
|
| 26 |
+
padding: 10px;
|
| 27 |
+
border-radius: 5px;
|
| 28 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
| 29 |
+
}
|
| 30 |
+
.comic-container {
|
| 31 |
+
display: flex;
|
| 32 |
+
justify-content: space-between;
|
| 33 |
+
margin-top: 20px;
|
| 34 |
+
}
|
| 35 |
+
.panel {
|
| 36 |
+
width: 23%;
|
| 37 |
+
background-color: white;
|
| 38 |
+
border: 1px solid #ccc;
|
| 39 |
+
padding: 10px;
|
| 40 |
+
box-shadow: 0 0 5px rgba(0,0,0,0.1);
|
| 41 |
+
}
|
| 42 |
+
.panel-content {
|
| 43 |
+
width: 100%;
|
| 44 |
+
height: 200px;
|
| 45 |
+
border: 1px solid #999;
|
| 46 |
+
display: flex;
|
| 47 |
+
justify-content: center;
|
| 48 |
+
align-items: center;
|
| 49 |
+
font-size: 18px;
|
| 50 |
+
color: #999;
|
| 51 |
+
background-color: #f9f9f9;
|
| 52 |
+
overflow: hidden;
|
| 53 |
+
}
|
| 54 |
+
.panel-content img {
|
| 55 |
+
width: 100%;
|
| 56 |
+
height: 100%;
|
| 57 |
+
object-fit: cover;
|
| 58 |
+
}
|
| 59 |
+
.dialog-box {
|
| 60 |
+
width: 95%;
|
| 61 |
+
min-height: 50px;
|
| 62 |
+
margin: 0 auto 10px;
|
| 63 |
+
padding: 10px;
|
| 64 |
+
background-color: #fff;
|
| 65 |
+
border: 2px solid #000;
|
| 66 |
+
border-radius: 15px;
|
| 67 |
+
font-size: 14px;
|
| 68 |
+
word-wrap: break-word;
|
| 69 |
+
position: relative;
|
| 70 |
+
}
|
| 71 |
+
.dialog-box:after {
|
| 72 |
+
content: "";
|
| 73 |
+
position: absolute;
|
| 74 |
+
top: 100%;
|
| 75 |
+
left: 50%;
|
| 76 |
+
margin-left: -10px;
|
| 77 |
+
border-width: 10px;
|
| 78 |
+
border-style: solid;
|
| 79 |
+
border-color: #000 transparent transparent transparent;
|
| 80 |
+
}
|
| 81 |
+
.dialog-box .speaker {
|
| 82 |
+
font-weight: bold;
|
| 83 |
+
}
|
| 84 |
+
.dialog-box .speech {
|
| 85 |
+
font-style: italic;
|
| 86 |
+
color: #0066cc;
|
| 87 |
+
}
|
| 88 |
+
textarea {
|
| 89 |
+
width: 100%;
|
| 90 |
+
height: 60px;
|
| 91 |
+
margin-top: 10px;
|
| 92 |
+
resize: vertical;
|
| 93 |
+
transition: opacity 0.3s ease;
|
| 94 |
+
}
|
| 95 |
+
select {
|
| 96 |
+
padding: 5px;
|
| 97 |
+
font-size: 14px;
|
| 98 |
+
width: 200px;
|
| 99 |
+
}
|
| 100 |
+
.button-container {
|
| 101 |
+
display: flex;
|
| 102 |
+
justify-content: space-between;
|
| 103 |
+
margin-top: 20px;
|
| 104 |
+
}
|
| 105 |
+
button {
|
| 106 |
+
padding: 10px;
|
| 107 |
+
font-size: 16px;
|
| 108 |
+
background-color: #4CAF50;
|
| 109 |
+
color: white;
|
| 110 |
+
border: none;
|
| 111 |
+
cursor: pointer;
|
| 112 |
+
flex-grow: 1;
|
| 113 |
+
margin: 0 5px;
|
| 114 |
+
}
|
| 115 |
+
button:hover {
|
| 116 |
+
background-color: #45a049;
|
| 117 |
+
}
|
| 118 |
+
#auto-generate {
|
| 119 |
+
background-color: #3498db;
|
| 120 |
+
}
|
| 121 |
+
#auto-generate:hover {
|
| 122 |
+
background-color: #2980b9;
|
| 123 |
+
}
|
| 124 |
+
.url-display {
|
| 125 |
+
text-align: center;
|
| 126 |
+
margin-top: 20px;
|
| 127 |
+
font-size: 0.9em;
|
| 128 |
+
color: #666;
|
| 129 |
+
}
|
| 130 |
+
</style></head><body>
|
| 131 |
+
<h1>Dynamic Strip Comic Maker</h1>
|
| 132 |
+
<div class="top-container">
|
| 133 |
+
<select id="style-selector">
|
| 134 |
+
<option value="peanuts">Peanuts</option>
|
| 135 |
+
<option value="farside" selected>Far Side</option>
|
| 136 |
+
<option value="doonesbury">Doonesbury</option>
|
| 137 |
+
<option value="calvin">Calvin and Hobbes</option>
|
| 138 |
+
</select>
|
| 139 |
+
<div class="instructions">
|
| 140 |
+
To use: select your comic style, and a) either enter in some description and text in the bottom boxes and press Generate or b) clear all text and press Auto Generate to let the AI create the panels itself. Note: description is before the - and all dialog is inside brackets ().
|
| 141 |
+
</div>
|
| 142 |
+
</div>
|
| 143 |
+
<div class="comic-container">
|
| 144 |
+
<div class="panel">
|
| 145 |
+
<div id="dialog1" class="dialog-box"></div>
|
| 146 |
+
<div id="panel1" class="panel-content">
|
| 147 |
+
<img src="https://via.placeholder.com/350x200/E0F2F1/000?text=Far+Side+1:+Two+cows+in+a+field" alt="Far Side style comic panel 1: Two cows in a field" width="350" height="200">
|
| 148 |
+
</div>
|
| 149 |
+
<textarea id="text1" placeholder="Enter description - (dialog)">Two cows in a field - (Hey, did you hear about the new mad cow disease? It's udder madness!)</textarea>
|
| 150 |
+
</div>
|
| 151 |
+
<div class="panel">
|
| 152 |
+
<div id="dialog2" class="dialog-box"></div>
|
| 153 |
+
<div id="panel2" class="panel-content">
|
| 154 |
+
<img src="https://via.placeholder.com/350x200/E0F2F1/000?text=Far+Side+2:+Scientist+with+giant+microscope" alt="Far Side style comic panel 2: Scientist with giant microscope" width="350" height="200">
|
| 155 |
+
</div>
|
| 156 |
+
<textarea id="text2" placeholder="Enter description - (dialog)">Scientist with giant microscope - (I've done it! I've found the world's smallest particle... my paycheck!)</textarea>
|
| 157 |
+
</div>
|
| 158 |
+
<div class="panel">
|
| 159 |
+
<div id="dialog3" class="dialog-box"></div>
|
| 160 |
+
<div id="panel3" class="panel-content">
|
| 161 |
+
<img src="https://via.placeholder.com/350x200/E0F2F1/000?text=Far+Side+3:+Cavemen+discovering+fire" alt="Far Side style comic panel 3: Cavemen discovering fire" width="350" height="200">
|
| 162 |
+
</div>
|
| 163 |
+
<textarea id="text3" placeholder="Enter description - (dialog)">Cavemen discovering fire - (Great, now how do we update our insurance policy?)</textarea>
|
| 164 |
+
</div>
|
| 165 |
+
<div class="panel">
|
| 166 |
+
<div id="dialog4" class="dialog-box"></div>
|
| 167 |
+
<div id="panel4" class="panel-content">
|
| 168 |
+
<img src="https://via.placeholder.com/350x200/E0F2F1/000?text=Far+Side+4:+Aliens+observing+Earth" alt="Far Side style comic panel 4: Aliens observing Earth" width="350" height="200">
|
| 169 |
+
</div>
|
| 170 |
+
<textarea id="text4" placeholder="Enter description - (dialog)">Aliens observing Earth - (They call that dancing? No wonder they haven't made interstellar contact yet.)</textarea>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
<div class="button-container">
|
| 174 |
+
<button id="generate-comic">Generate</button>
|
| 175 |
+
<button id="auto-generate">Auto Generate</button>
|
| 176 |
+
</div>
|
| 177 |
+
<div class="url-display">
|
| 178 |
+
URL: <a href="https://websim.ai/comic-maker">https://websim.ai/comic-maker</a>
|
| 179 |
+
</div>
|
| 180 |
+
|
| 181 |
+
<script>
|
| 182 |
+
function generateImage(style, description, panelNumber) {
|
| 183 |
+
const styleBackgrounds = {
|
| 184 |
+
peanuts: "#FFF9C4",
|
| 185 |
+
farside: "#E0F2F1",
|
| 186 |
+
doonesbury: "#E8EAF6",
|
| 187 |
+
calvin: "#FFF3E0"
|
| 188 |
+
};
|
| 189 |
+
|
| 190 |
+
const styleColor = styleBackgrounds[style] || "#FFFFFF";
|
| 191 |
+
const textColor = style === 'farside' ? '000' : '333';
|
| 192 |
+
|
| 193 |
+
return `https://via.placeholder.com/350x200/${styleColor.substring(1)}/${textColor}?text=${encodeURIComponent(style + ' ' + panelNumber + ': ' + description)}`;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
function extractContent(text) {
|
| 197 |
+
const [description, dialogPart] = text.split('-').map(part => part.trim());
|
| 198 |
+
const dialogMatch = dialogPart ? dialogPart.match(/\((.*?)\)/) : null;
|
| 199 |
+
const dialog = dialogMatch ? dialogMatch[1] : '';
|
| 200 |
+
|
| 201 |
+
return { description, dialog };
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
function updatePanel(panelNumber, style, content) {
|
| 205 |
+
const panel = document.getElementById(`panel${panelNumber}`);
|
| 206 |
+
const dialog = document.getElementById(`dialog${panelNumber}`);
|
| 207 |
+
const textArea = document.getElementById(`text${panelNumber}`);
|
| 208 |
+
|
| 209 |
+
panel.innerHTML = '';
|
| 210 |
+
|
| 211 |
+
const img = document.createElement('img');
|
| 212 |
+
img.src = generateImage(style, content.description, panelNumber);
|
| 213 |
+
img.alt = `${style} style comic panel ${panelNumber}: ${content.description}`;
|
| 214 |
+
img.width = 350;
|
| 215 |
+
img.height = 200;
|
| 216 |
+
panel.appendChild(img);
|
| 217 |
+
|
| 218 |
+
dialog.innerHTML = `<span class="speech">${content.dialog}</span>`;
|
| 219 |
+
|
| 220 |
+
textArea.value = `${content.description} - (${content.dialog})`;
|
| 221 |
+
textArea.style.opacity = '0.75';
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
function generateComic() {
|
| 225 |
+
const style = document.getElementById('style-selector').value;
|
| 226 |
+
|
| 227 |
+
for (let i = 1; i <= 4; i++) {
|
| 228 |
+
const text = document.getElementById(`text${i}`).value.trim();
|
| 229 |
+
if (text === '') {
|
| 230 |
+
alert(`Please enter text for panel ${i} before generating the comic.`);
|
| 231 |
+
return;
|
| 232 |
+
}
|
| 233 |
+
const content = extractContent(text);
|
| 234 |
+
updatePanel(i, style, content);
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
alert('Comic generated using user input!');
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
function generateRandomPanel(style) {
|
| 241 |
+
const themes = {
|
| 242 |
+
peanuts: [
|
| 243 |
+
{ description: "Charlie Brown missing football kick", dialog: "I can't believe I fell for it again!" },
|
| 244 |
+
{ description: "Snoopy as World War I Flying Ace", dialog: "Curse you, Red Baron!" },
|
| 245 |
+
{ description: "Linus with security blanket", dialog: "It's not a blanket, it's a portable security system." },
|
| 246 |
+
{ description: "Lucy at psychiatry booth", dialog: "The doctor is in. Five cents, please." },
|
| 247 |
+
{ description: "Schroeder playing piano", dialog: "Beethoven is my muse." },
|
| 248 |
+
{ description: "Peppermint Patty sleeping in class", dialog: "I wasn't sleeping, I was just resting my eyes!" }
|
| 249 |
+
],
|
| 250 |
+
farside: [
|
| 251 |
+
{ description: "Cows plotting world domination", dialog: "First the farm, then the world!" },
|
| 252 |
+
{ description: "Scientist creating doomsday device", dialog: "What could possibly go wrong?" },
|
| 253 |
+
{ description: "Alien tourist trap on Earth", dialog: "Two for one abductions today!" },
|
| 254 |
+
{ description: "Cavemen inventing something ridiculous", dialog: "I call it... the 'selfie stick'!" },
|
| 255 |
+
{ description: "Animals in therapy session", dialog: "Tell me about your relationship with your owner." },
|
| 256 |
+
{ description: "God playing with Earth snow globe", dialog: "Oops, I think I shook it too hard." }
|
| 257 |
+
],
|
| 258 |
+
doonesbury: [
|
| 259 |
+
{ description: "Mike Doonesbury reading news", dialog: "Breaking: Reality still disappointing." },
|
| 260 |
+
{ description: "Zonker tanning", dialog: "Is it summer yet? I can't tell from in here." },
|
| 261 |
+
{ description: "Uncle Duke hallucinating", dialog: "The bats! They're everywhere!" },
|
| 262 |
+
{ description: "Joanie Caucus at political rally", dialog: "Another day, another protest. Pass the megaphone." },
|
| 263 |
+
{ description: "B.D. without helmet", dialog: "I feel so... exposed." },
|
| 264 |
+
{ description: "Roland Hedley reporting fake news", dialog: "This just in: Truth now optional!" }
|
| 265 |
+
],
|
| 266 |
+
calvin: [
|
| 267 |
+
{ description: "Calvin and Hobbes sledding", dialog: "I think I can see my house from heeeeere!" },
|
| 268 |
+
{ description: "Spaceman Spiff crash landing", dialog: "Zounds! Alien terrain dead ahead!" },
|
| 269 |
+
{ description: "Calvin's dad explaining something absurd", dialog: "It builds character, Calvin." },
|
| 270 |
+
{ description: "Calvin as Tracer Bullet, detective", dialog: "The dame was trouble, but I was too." },
|
| 271 |
+
{ description: "Calvin's snowmen art", dialog: "I call it 'The Torment of Existence Weighed Against the Horror of Nonbeing'." },
|
| 272 |
+
{ description: "Hobbes pouncing on Calvin", dialog: "Gotcha! You need to be more aware of your surroundings." }
|
| 273 |
+
]
|
| 274 |
+
};
|
| 275 |
+
|
| 276 |
+
const randomIndex = Math.floor(Math.random() * themes[style].length);
|
| 277 |
+
return themes[style][randomIndex];
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
function autoGenerateComic() {
|
| 281 |
+
const style = document.getElementById('style-selector').value;
|
| 282 |
+
|
| 283 |
+
for (let i = 1; i <= 4; i++) {
|
| 284 |
+
const content = generateRandomPanel(style);
|
| 285 |
+
updatePanel(i, style, content);
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
alert('New comic auto-generated using AI!');
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
document.getElementById('generate-comic').addEventListener('click', generateComic);
|
| 292 |
+
document.getElementById('auto-generate').addEventListener('click', autoGenerateComic);
|
| 293 |
+
</script>
|
| 294 |
+
</body></html>
|
UZNY2tEsXhS8RqUM9.html
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://leafletexample.com/">
|
| 2 |
+
<title>Interactive Hiking Route Planner</title>
|
| 3 |
+
<meta charset="utf-8">
|
| 4 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 5 |
+
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
|
| 6 |
+
<style>
|
| 7 |
+
#map {
|
| 8 |
+
height: 100%;
|
| 9 |
+
}
|
| 10 |
+
html, body {
|
| 11 |
+
height: 100%;
|
| 12 |
+
margin: 0;
|
| 13 |
+
padding: 0;
|
| 14 |
+
}
|
| 15 |
+
.button-container {
|
| 16 |
+
position: absolute;
|
| 17 |
+
top: 10px;
|
| 18 |
+
right: 10px;
|
| 19 |
+
z-index: 1000;
|
| 20 |
+
}
|
| 21 |
+
.map-button, .layer-button {
|
| 22 |
+
display: block;
|
| 23 |
+
width: 140px;
|
| 24 |
+
padding: 5px;
|
| 25 |
+
border: 1px solid #ccc;
|
| 26 |
+
border-radius: 5px;
|
| 27 |
+
text-align: center;
|
| 28 |
+
background: white;
|
| 29 |
+
cursor: pointer;
|
| 30 |
+
margin-bottom: 5px;
|
| 31 |
+
}
|
| 32 |
+
.map-button:hover, .layer-button:hover {
|
| 33 |
+
background: #f4f4f4;
|
| 34 |
+
}
|
| 35 |
+
.distance-marker {
|
| 36 |
+
font-size: 12px;
|
| 37 |
+
font-weight: bold;
|
| 38 |
+
text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white;
|
| 39 |
+
}
|
| 40 |
+
</style>
|
| 41 |
+
</head>
|
| 42 |
+
<body>
|
| 43 |
+
<div id="map"></div>
|
| 44 |
+
<div class="button-container">
|
| 45 |
+
<div id="clearPinsButton" class="map-button">Clear Pins</div>
|
| 46 |
+
<div id="undoLastPinButton" class="map-button">Undo Last Pin</div>
|
| 47 |
+
<div id="toggleContoursButton" class="layer-button">Toggle Contour Lines</div>
|
| 48 |
+
<div id="toggleSatelliteButton" class="layer-button">Toggle Satellite</div>
|
| 49 |
+
<div id="toggleUSGSTopoButton" class="layer-button">Toggle USGS Topo Map</div>
|
| 50 |
+
</div>
|
| 51 |
+
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
| 52 |
+
<script src="https://unpkg.com/esri-leaflet/dist/esri-leaflet.js"></script>
|
| 53 |
+
<script>
|
| 54 |
+
var map = L.map('map').setView([39.8283, -98.5795], 5);
|
| 55 |
+
|
| 56 |
+
var openStreetMap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
| 57 |
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
| 58 |
+
}).addTo(map);
|
| 59 |
+
|
| 60 |
+
var esriWorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
| 61 |
+
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
var usgsTopoLayer = L.esri.tiledMapLayer({
|
| 65 |
+
url: "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer",
|
| 66 |
+
maxZoom: 15
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
var contoursLayer = L.esri.tiledMapLayer({
|
| 70 |
+
url: "https://basemap.nationalmap.gov/arcgis/rest/services/USGSHydroCached/MapServer",
|
| 71 |
+
maxZoom: 15
|
| 72 |
+
});
|
| 73 |
+
|
| 74 |
+
var baseMaps = {
|
| 75 |
+
"OpenStreetMap": openStreetMap,
|
| 76 |
+
"Satellite Imagery": esriWorldImagery,
|
| 77 |
+
"USGS Topo Map": usgsTopoLayer
|
| 78 |
+
};
|
| 79 |
+
|
| 80 |
+
var overlayMaps = {
|
| 81 |
+
"Contour Lines": contoursLayer
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
+
L.control.layers(baseMaps, overlayMaps).addTo(map);
|
| 85 |
+
|
| 86 |
+
var popup = L.popup();
|
| 87 |
+
|
| 88 |
+
function onMapClick(e) {
|
| 89 |
+
popup
|
| 90 |
+
.setLatLng(e.latlng)
|
| 91 |
+
.setContent("Latitude: " + e.latlng.lat.toFixed(4) + "<br>Longitude: " + e.latlng.lng.toFixed(4) + "<br>Elevation: <span id='elevationValue'>Fetching...</span>")
|
| 92 |
+
.openOn(map);
|
| 93 |
+
|
| 94 |
+
getElevation(e.latlng.lat, e.latlng.lng);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
map.on('click', onMapClick);
|
| 98 |
+
|
| 99 |
+
var waypoints = [];
|
| 100 |
+
var waypointMarkers = L.layerGroup().addTo(map);
|
| 101 |
+
var waypointLines = L.polyline([], {color: 'red'}).addTo(map);
|
| 102 |
+
var distanceMarkers = L.layerGroup().addTo(map);
|
| 103 |
+
|
| 104 |
+
function onMapRightClick(e) {
|
| 105 |
+
var marker = L.marker(e.latlng).addTo(waypointMarkers);
|
| 106 |
+
waypoints.push(e.latlng);
|
| 107 |
+
marker.bindPopup("Waypoint " + waypoints.length).openPopup();
|
| 108 |
+
|
| 109 |
+
if (waypoints.length > 1) {
|
| 110 |
+
waypointLines.addLatLng(e.latlng);
|
| 111 |
+
|
| 112 |
+
var latLngs = waypointLines.getLatLngs();
|
| 113 |
+
var lastLatLng = latLngs[latLngs.length - 2];
|
| 114 |
+
var dist = lastLatLng.distanceTo(e.latlng) * 0.000621371; // convert to miles
|
| 115 |
+
var midPoint = L.latLng(
|
| 116 |
+
(lastLatLng.lat + e.latlng.lat) / 2,
|
| 117 |
+
(lastLatLng.lng + e.latlng.lng) / 2
|
| 118 |
+
);
|
| 119 |
+
var distanceMarker = L.marker(midPoint, {
|
| 120 |
+
icon: L.divIcon({
|
| 121 |
+
className: 'distance-marker',
|
| 122 |
+
html: dist.toFixed(1) + ' mi',
|
| 123 |
+
iconSize: [50, 12]
|
| 124 |
+
})
|
| 125 |
+
});
|
| 126 |
+
distanceMarkers.addLayer(distanceMarker);
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
map.on('contextmenu', onMapRightClick);
|
| 131 |
+
|
| 132 |
+
document.getElementById('clearPinsButton').onclick = function() {
|
| 133 |
+
waypoints = [];
|
| 134 |
+
waypointMarkers.clearLayers();
|
| 135 |
+
waypointLines.setLatLngs([]);
|
| 136 |
+
distanceMarkers.clearLayers();
|
| 137 |
+
};
|
| 138 |
+
|
| 139 |
+
document.getElementById('undoLastPinButton').onclick = function() {
|
| 140 |
+
if (waypoints.length > 0) {
|
| 141 |
+
waypoints.pop();
|
| 142 |
+
waypointMarkers.removeLayer(waypointMarkers.getLayers()[waypointMarkers.getLayers().length - 1]);
|
| 143 |
+
|
| 144 |
+
if (waypoints.length > 0) {
|
| 145 |
+
waypointLines.setLatLngs(waypoints);
|
| 146 |
+
} else {
|
| 147 |
+
waypointLines.setLatLngs([]);
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
distanceMarkers.removeLayer(distanceMarkers.getLayers()[distanceMarkers.getLayers().length - 1]);
|
| 151 |
+
}
|
| 152 |
+
};
|
| 153 |
+
|
| 154 |
+
document.getElementById('toggleContoursButton').onclick = function() {
|
| 155 |
+
if (map.hasLayer(contoursLayer)) {
|
| 156 |
+
map.removeLayer(contoursLayer);
|
| 157 |
+
} else {
|
| 158 |
+
contoursLayer.addTo(map);
|
| 159 |
+
}
|
| 160 |
+
};
|
| 161 |
+
|
| 162 |
+
document.getElementById('toggleSatelliteButton').onclick = function() {
|
| 163 |
+
if (map.hasLayer(esriWorldImagery)) {
|
| 164 |
+
map.removeLayer(esriWorldImagery);
|
| 165 |
+
if (!map.hasLayer(openStreetMap) && !map.hasLayer(usgsTopoLayer)) {
|
| 166 |
+
openStreetMap.addTo(map);
|
| 167 |
+
}
|
| 168 |
+
} else {
|
| 169 |
+
esriWorldImagery.addTo(map);
|
| 170 |
+
if (map.hasLayer(openStreetMap)) {
|
| 171 |
+
map.removeLayer(openStreetMap);
|
| 172 |
+
}
|
| 173 |
+
if (map.hasLayer(usgsTopoLayer)) {
|
| 174 |
+
map.removeLayer(usgsTopoLayer);
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
};
|
| 178 |
+
|
| 179 |
+
document.getElementById('toggleUSGSTopoButton').onclick = function() {
|
| 180 |
+
if (map.hasLayer(usgsTopoLayer)) {
|
| 181 |
+
map.removeLayer(usgsTopoLayer);
|
| 182 |
+
if (!map.hasLayer(openStreetMap) && !map.hasLayer(esriWorldImagery)) {
|
| 183 |
+
openStreetMap.addTo(map);
|
| 184 |
+
}
|
| 185 |
+
} else {
|
| 186 |
+
usgsTopoLayer.addTo(map);
|
| 187 |
+
if (map.hasLayer(openStreetMap)) {
|
| 188 |
+
map.removeLayer(openStreetMap);
|
| 189 |
+
}
|
| 190 |
+
if (map.hasLayer(esriWorldImagery)) {
|
| 191 |
+
map.removeLayer(esriWorldImagery);
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
+
};
|
| 195 |
+
|
| 196 |
+
function getElevation(lat, lng) {
|
| 197 |
+
fetch('https://nationalmap.gov/epqs/pqs.php?x=' + lng + '&y=' + lat + '&units=Feet&output=json')
|
| 198 |
+
.then(response => response.json())
|
| 199 |
+
.then(data => {
|
| 200 |
+
document.getElementById('elevationValue').innerHTML = data.USGS_Elevation_Point_Query_Service.Elevation_Query.Elevation + ' ft';
|
| 201 |
+
});
|
| 202 |
+
}
|
| 203 |
+
</script>
|
| 204 |
+
|
| 205 |
+
<div id="bookmarks-bar" class="flex justify-between items-center flex-wrap p-2">
|
| 206 |
+
<div class="flex space-x-4">
|
| 207 |
+
<a href="/" class="flex items-center space-x-1 text-black hover:text-blue-600">
|
| 208 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 209 |
+
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
|
| 210 |
+
<polyline points="9 22 9 12 15 12 15 22"></polyline>
|
| 211 |
+
</svg>
|
| 212 |
+
<span>Home</span>
|
| 213 |
+
</a>
|
| 214 |
+
<a href="/maps?topic=fantasy" class="flex items-center space-x-1 text-black hover:text-blue-600">
|
| 215 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 216 |
+
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
|
| 217 |
+
<circle cx="12" cy="10" r="3"></circle>
|
| 218 |
+
</svg>
|
| 219 |
+
<span>Fantasy Maps</span>
|
| 220 |
+
</a>
|
| 221 |
+
<a href="/maps?topic=historical" class="flex items-center space-x-1 text-black hover:text-blue-600">
|
| 222 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 223 |
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
|
| 224 |
+
</svg>
|
| 225 |
+
<span>Historical Maps</span>
|
| 226 |
+
</a>
|
| 227 |
+
</div>
|
| 228 |
+
</div>
|
| 229 |
+
|
| 230 |
+
<div class="max-w-screen-xl mx-auto p-6">
|
| 231 |
+
<h1 class="text-2xl font-bold mb-4">Hiking Route Planner</h1>
|
| 232 |
+
|
| 233 |
+
<p class="mb-4">
|
| 234 |
+
Plan your hikes with this interactive route mapping application.
|
| 235 |
+
<ul class="list-disc pl-6 mb-4">
|
| 236 |
+
<li>Click the map to see latitude, longitude and elevation at that point</li>
|
| 237 |
+
<li>Right click to place waypoints along your route</li>
|
| 238 |
+
<li>See the distance between waypoints in miles</li>
|
| 239 |
+
<li>Clear all pins or undo the last pin with the buttons in the top right</li>
|
| 240 |
+
<li>Toggle contour lines, satellite imagery, and USGS topo maps on/off</li>
|
| 241 |
+
<li>Use the layer control to change the base map</li>
|
| 242 |
+
</ul>
|
| 243 |
+
Start mapping out your next hiking adventure!
|
| 244 |
+
</p>
|
| 245 |
+
|
| 246 |
+
<h2 class="text-xl font-semibold mt-8 mb-4">About This Map</h2>
|
| 247 |
+
<p class="mb-4">
|
| 248 |
+
The map uses:
|
| 249 |
+
<ul class="list-disc list-inside mb-4">
|
| 250 |
+
<li><a href="https://leafletjs.com/" class="text-blue-600 hover:underline">Leaflet</a> for the interactive map</li>
|
| 251 |
+
<li><a href="https://esri.github.io/esri-leaflet/" class="text-blue-600 hover:underline">Esri Leaflet</a> for USGS layers (contours, topo)</li>
|
| 252 |
+
<li><a href="https://www.openstreetmap.org/" class="text-blue-600 hover:underline">OpenStreetMap</a>, <a href="https://www.esri.com/en-us/arcgis/products/imagery/overview" class="text-blue-600 hover:underline">Esri Satellite Imagery</a>, and <a href="https://www.usgs.gov/core-science-systems/national-geospatial-program/national-map" class="text-blue-600 hover:underline">USGS National Map</a> for base maps</li>
|
| 253 |
+
<li><a href="https://www.usgs.gov/core-science-systems/ngp/tnm-delivery/gis-data-download" class="text-blue-600 hover:underline">USGS Elevation Point Query Service</a> for elevation data</li>
|
| 254 |
+
</ul>
|
| 255 |
+
</p>
|
| 256 |
+
|
| 257 |
+
<h2 class="text-xl font-semibold mt-8 mb-4">Happy Trails!</h2>
|
| 258 |
+
<p>We hope you find this tool useful for planning your outdoor adventures. Always remember to practice <a href="https://lnt.org/why/7-principles/" class="text-blue-600 hover:underline">Leave No Trace principles</a> and check local regulations before heading out. Have fun and stay safe out there!</p>
|
| 259 |
+
|
| 260 |
+
</div>
|
| 261 |
+
|
| 262 |
+
</body></html>
|
ZEqZXcZpfUDkV8V2M.html
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://vorpal.strikes/ascii-photo"><style>
|
| 2 |
+
@import url('https://fonts.googleapis.com/css2?family=Orbitron&display=swap');
|
| 3 |
+
|
| 4 |
+
:root {
|
| 5 |
+
--bg-color: #0a0a0a;
|
| 6 |
+
--fg-color: #0ff;
|
| 7 |
+
--accent-color: #0cc;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
* {
|
| 11 |
+
box-sizing: border-box;
|
| 12 |
+
transition: all 0.3s ease;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
body {
|
| 16 |
+
margin: 0;
|
| 17 |
+
padding: 20px;
|
| 18 |
+
font-family: 'Orbitron', sans-serif;
|
| 19 |
+
background-color: var(--bg-color);
|
| 20 |
+
color: var(--fg-color);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
h1, h2 {
|
| 24 |
+
text-align: center;
|
| 25 |
+
color: var(--accent-color);
|
| 26 |
+
text-shadow: 0 0 10px var(--accent-color);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
#ascii-art {
|
| 30 |
+
font-family: monospace;
|
| 31 |
+
white-space: pre;
|
| 32 |
+
overflow: auto;
|
| 33 |
+
max-width: 100%;
|
| 34 |
+
max-height: 70vh;
|
| 35 |
+
border: 2px solid var(--accent-color);
|
| 36 |
+
border-radius: 10px;
|
| 37 |
+
padding: 10px;
|
| 38 |
+
box-shadow: 0 0 10px var(--accent-color);
|
| 39 |
+
background-color: rgba(0, 0, 0, 0.7);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
#photo-container {
|
| 43 |
+
display: flex;
|
| 44 |
+
justify-content: center;
|
| 45 |
+
margin-bottom: 20px;
|
| 46 |
+
position: relative;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
#photo {
|
| 50 |
+
max-width: 100%;
|
| 51 |
+
max-height: 500px;
|
| 52 |
+
border-radius: 10px;
|
| 53 |
+
box-shadow: 0 0 20px var(--fg-color);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
.controls {
|
| 57 |
+
display: flex;
|
| 58 |
+
flex-wrap: wrap;
|
| 59 |
+
justify-content: center;
|
| 60 |
+
gap: 10px;
|
| 61 |
+
margin-bottom: 20px;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
button, select, input {
|
| 65 |
+
padding: 10px 20px;
|
| 66 |
+
font-size: 16px;
|
| 67 |
+
border: 2px solid var(--accent-color);
|
| 68 |
+
border-radius: 5px;
|
| 69 |
+
background-color: var(--bg-color);
|
| 70 |
+
color: var(--fg-color);
|
| 71 |
+
font-family: 'Orbitron', sans-serif;
|
| 72 |
+
cursor: pointer;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
button:hover, select:hover {
|
| 76 |
+
background-color: var(--accent-color);
|
| 77 |
+
color: var(--bg-color);
|
| 78 |
+
box-shadow: 0 0 10px var(--accent-color);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
button:disabled {
|
| 82 |
+
opacity: 0.5;
|
| 83 |
+
cursor: not-allowed;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.glitch {
|
| 87 |
+
animation: glitch 1s linear infinite;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
@keyframes glitch {
|
| 91 |
+
2%, 64% {
|
| 92 |
+
transform: translate(2px, 0) skew(0deg);
|
| 93 |
+
}
|
| 94 |
+
4%, 60% {
|
| 95 |
+
transform: translate(-2px, 0) skew(0deg);
|
| 96 |
+
}
|
| 97 |
+
62% {
|
| 98 |
+
transform: translate(0, 0) skew(5deg);
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
</style>
|
| 102 |
+
</head>
|
| 103 |
+
<body>
|
| 104 |
+
<h1 class="glitch">Photo to ASCII Art Converter</h1>
|
| 105 |
+
<div id="photo-container">
|
| 106 |
+
<img id="photo" src="" alt="Your photo will appear here">
|
| 107 |
+
</div>
|
| 108 |
+
<div class="controls">
|
| 109 |
+
<button id="upload-photo">Upload Photo</button>
|
| 110 |
+
<input type="file" id="photo-input" accept="image/*" style="display: none;">
|
| 111 |
+
<button id="toggle-ascii" disabled>Start ASCII Conversion</button>
|
| 112 |
+
<button id="copy-text">Copy Text</button>
|
| 113 |
+
<select id="char-select">
|
| 114 |
+
<option value=" .,:;=!*#$@">Density 1</option>
|
| 115 |
+
<option value="@%#*+=-:. ">Density 2</option>
|
| 116 |
+
<option value="#WMBRXVYIti+=;:,. ">Density 3</option>
|
| 117 |
+
</select>
|
| 118 |
+
<select id="char-type-select">
|
| 119 |
+
<option value="standard">Standard</option>
|
| 120 |
+
<option value="blocks">Blocks</option>
|
| 121 |
+
<option value="braille">Braille</option>
|
| 122 |
+
</select>
|
| 123 |
+
<select id="color-mode-select">
|
| 124 |
+
<option value="mono">Monochrome</option>
|
| 125 |
+
<option value="live">Live Color</option>
|
| 126 |
+
</select>
|
| 127 |
+
<input type="range" id="resolution-input" min="0.05" max="1" step="0.05" value="0.15">
|
| 128 |
+
</div>
|
| 129 |
+
<pre id="ascii-art"></pre>
|
| 130 |
+
|
| 131 |
+
<footer>made with love by @vorpal_strikes</footer>
|
| 132 |
+
|
| 133 |
+
<script>
|
| 134 |
+
const photoContainer = document.getElementById('photo-container');
|
| 135 |
+
const photo = document.getElementById('photo');
|
| 136 |
+
const uploadBtn = document.getElementById('upload-photo');
|
| 137 |
+
const photoInput = document.getElementById('photo-input');
|
| 138 |
+
const toggleBtn = document.getElementById('toggle-ascii');
|
| 139 |
+
const copyTextBtn = document.getElementById('copy-text');
|
| 140 |
+
const asciiArt = document.getElementById('ascii-art');
|
| 141 |
+
const charSelect = document.getElementById('char-select');
|
| 142 |
+
const charTypeSelect = document.getElementById('char-type-select');
|
| 143 |
+
const colorModeSelect = document.getElementById('color-mode-select');
|
| 144 |
+
const resolutionInput = document.getElementById('resolution-input');
|
| 145 |
+
|
| 146 |
+
let capturing = false;
|
| 147 |
+
|
| 148 |
+
const charSets = {
|
| 149 |
+
standard: {
|
| 150 |
+
" .,:;=!*#$@": " .,:;=!*#$@",
|
| 151 |
+
"@%#*+=-:. ": "@%#*+=-:. ",
|
| 152 |
+
"#WMBRXVYIti+=;:,. ": "#WMBRXVYIti+=;:,. ",
|
| 153 |
+
},
|
| 154 |
+
blocks: {
|
| 155 |
+
" .,:;=!*#$@": " ░▒▓█",
|
| 156 |
+
"@%#*+=-:. ": " ▂▄▆█",
|
| 157 |
+
"#WMBRXVYIti+=;:,. ": " ▖▗▘▙▚▛▜▝▞▟",
|
| 158 |
+
},
|
| 159 |
+
braille: {
|
| 160 |
+
" .,:;=!*#$@": " ⠁⠂⠄⡀⡄⡆⡇⡧⣿",
|
| 161 |
+
"@%#*+=-:. ": " ⠈⠉⠋⠓⠒⠲⠦⠔⠴⣿",
|
| 162 |
+
"#WMBRXVYIti+=;:,. ": " ⠁⠂⠄⡀⡄⡆⢀⣀⣄⣆⣇⡧⡷⣿",
|
| 163 |
+
},
|
| 164 |
+
};
|
| 165 |
+
|
| 166 |
+
function photoToAscii() {
|
| 167 |
+
const canvas = document.createElement('canvas');
|
| 168 |
+
const ctx = canvas.getContext('2d');
|
| 169 |
+
|
| 170 |
+
const scale = Number(resolutionInput.value);
|
| 171 |
+
canvas.width = photo.width * scale;
|
| 172 |
+
canvas.height = photo.height * scale;
|
| 173 |
+
|
| 174 |
+
ctx.drawImage(photo, 0, 0, canvas.width, canvas.height);
|
| 175 |
+
|
| 176 |
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
| 177 |
+
const data = imageData.data;
|
| 178 |
+
|
| 179 |
+
const charSet = charSets[charTypeSelect.value][charSelect.value];
|
| 180 |
+
const colorMode = colorModeSelect.value;
|
| 181 |
+
|
| 182 |
+
let ascii = '';
|
| 183 |
+
|
| 184 |
+
for (let i = 0; i < data.length; i += 4) {
|
| 185 |
+
const r = data[i];
|
| 186 |
+
const g = data[i + 1];
|
| 187 |
+
const b = data[i + 2];
|
| 188 |
+
|
| 189 |
+
const brightness = (r + g + b) / 3;
|
| 190 |
+
const charIndex = Math.floor(brightness / 256 * charSet.length);
|
| 191 |
+
|
| 192 |
+
if (colorMode === 'live') {
|
| 193 |
+
ascii += `<span style="color: rgb(${r}, ${g}, ${b})">${charSet[charIndex]}</span>`;
|
| 194 |
+
} else {
|
| 195 |
+
ascii += charSet[charIndex];
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
if ((i / 4 + 1) % canvas.width === 0) {
|
| 199 |
+
ascii += '\n';
|
| 200 |
+
}
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
asciiArt.innerHTML = ascii;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
uploadBtn.addEventListener('click', () => {
|
| 207 |
+
photoInput.click();
|
| 208 |
+
});
|
| 209 |
+
|
| 210 |
+
photoInput.addEventListener('change', () => {
|
| 211 |
+
const file = photoInput.files[0];
|
| 212 |
+
const reader = new FileReader();
|
| 213 |
+
|
| 214 |
+
reader.onload = () => {
|
| 215 |
+
photo.src = reader.result;
|
| 216 |
+
toggleBtn.disabled = false;
|
| 217 |
+
};
|
| 218 |
+
|
| 219 |
+
reader.readAsDataURL(file);
|
| 220 |
+
});
|
| 221 |
+
|
| 222 |
+
toggleBtn.addEventListener('click', () => {
|
| 223 |
+
capturing = !capturing;
|
| 224 |
+
if (capturing) {
|
| 225 |
+
photoToAscii();
|
| 226 |
+
toggleBtn.textContent = 'Stop ASCII Conversion';
|
| 227 |
+
} else {
|
| 228 |
+
asciiArt.innerHTML = '';
|
| 229 |
+
toggleBtn.textContent = 'Start ASCII Conversion';
|
| 230 |
+
}
|
| 231 |
+
});
|
| 232 |
+
|
| 233 |
+
copyTextBtn.addEventListener('click', () => {
|
| 234 |
+
const tempTextarea = document.createElement('textarea');
|
| 235 |
+
tempTextarea.value = asciiArt.innerText;
|
| 236 |
+
document.body.appendChild(tempTextarea);
|
| 237 |
+
tempTextarea.select();
|
| 238 |
+
document.execCommand('copy');
|
| 239 |
+
document.body.removeChild(tempTextarea);
|
| 240 |
+
alert('ASCII art copied to clipboard!');
|
| 241 |
+
});
|
| 242 |
+
|
| 243 |
+
charSelect.addEventListener('change', photoToAscii);
|
| 244 |
+
charTypeSelect.addEventListener('change', photoToAscii);
|
| 245 |
+
colorModeSelect.addEventListener('change', photoToAscii);
|
| 246 |
+
resolutionInput.addEventListener('input', photoToAscii);
|
| 247 |
+
</script>
|
| 248 |
+
</body></html>
|
cYUneFx9tfHZWLOXx.html
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="/draw"><title>Wackcom Drawing Labs - Interactive Canvas</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 5 |
+
margin: 0;
|
| 6 |
+
padding: 0;
|
| 7 |
+
background-color: #f0f5ff;
|
| 8 |
+
display: flex;
|
| 9 |
+
flex-direction: column;
|
| 10 |
+
height: 100vh;
|
| 11 |
+
}
|
| 12 |
+
.header {
|
| 13 |
+
background-color: #3498db;
|
| 14 |
+
color: white;
|
| 15 |
+
padding: 10px 20px;
|
| 16 |
+
display: flex;
|
| 17 |
+
justify-content: space-between;
|
| 18 |
+
align-items: center;
|
| 19 |
+
}
|
| 20 |
+
.logo {
|
| 21 |
+
font-size: 1.5em;
|
| 22 |
+
font-weight: bold;
|
| 23 |
+
}
|
| 24 |
+
.main-content {
|
| 25 |
+
display: flex;
|
| 26 |
+
flex-grow: 1;
|
| 27 |
+
}
|
| 28 |
+
.toolbar {
|
| 29 |
+
width: 200px;
|
| 30 |
+
background-color: #f9f9f9;
|
| 31 |
+
padding: 20px;
|
| 32 |
+
display: flex;
|
| 33 |
+
flex-direction: column;
|
| 34 |
+
gap: 10px;
|
| 35 |
+
}
|
| 36 |
+
.canvas-container {
|
| 37 |
+
flex-grow: 1;
|
| 38 |
+
display: flex;
|
| 39 |
+
justify-content: center;
|
| 40 |
+
align-items: center;
|
| 41 |
+
background-color: #ffffff;
|
| 42 |
+
}
|
| 43 |
+
canvas {
|
| 44 |
+
border: 1px solid #ddd;
|
| 45 |
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
| 46 |
+
}
|
| 47 |
+
button {
|
| 48 |
+
padding: 10px;
|
| 49 |
+
background-color: #3498db;
|
| 50 |
+
color: white;
|
| 51 |
+
border: none;
|
| 52 |
+
border-radius: 5px;
|
| 53 |
+
cursor: pointer;
|
| 54 |
+
transition: background-color 0.3s ease;
|
| 55 |
+
}
|
| 56 |
+
button:hover {
|
| 57 |
+
background-color: #2980b9;
|
| 58 |
+
}
|
| 59 |
+
.color-picker {
|
| 60 |
+
display: flex;
|
| 61 |
+
flex-wrap: wrap;
|
| 62 |
+
gap: 5px;
|
| 63 |
+
}
|
| 64 |
+
.color-swatch {
|
| 65 |
+
width: 30px;
|
| 66 |
+
height: 30px;
|
| 67 |
+
border-radius: 50%;
|
| 68 |
+
cursor: pointer;
|
| 69 |
+
}
|
| 70 |
+
.brush-size {
|
| 71 |
+
display: flex;
|
| 72 |
+
align-items: center;
|
| 73 |
+
}
|
| 74 |
+
#brushSize {
|
| 75 |
+
width: 100%;
|
| 76 |
+
}
|
| 77 |
+
.ai-tools {
|
| 78 |
+
margin-top: 20px;
|
| 79 |
+
}
|
| 80 |
+
</style>
|
| 81 |
+
</head>
|
| 82 |
+
<body>
|
| 83 |
+
<div class="header">
|
| 84 |
+
<div class="logo">Wackcom Drawing Labs</div>
|
| 85 |
+
<button id="saveBtn">Save Artwork</button>
|
| 86 |
+
</div>
|
| 87 |
+
<div class="main-content">
|
| 88 |
+
<div class="toolbar">
|
| 89 |
+
<button id="pencilBtn">Pencil</button>
|
| 90 |
+
<button id="brushBtn">Brush</button>
|
| 91 |
+
<button id="eraserBtn">Eraser</button>
|
| 92 |
+
<div class="color-picker">
|
| 93 |
+
<div class="color-swatch" style="background-color: #000000;"></div>
|
| 94 |
+
<div class="color-swatch" style="background-color: #ff0000;"></div>
|
| 95 |
+
<div class="color-swatch" style="background-color: #00ff00;"></div>
|
| 96 |
+
<div class="color-swatch" style="background-color: #0000ff;"></div>
|
| 97 |
+
<div class="color-swatch" style="background-color: #ffff00;"></div>
|
| 98 |
+
<div class="color-swatch" style="background-color: #ff00ff;"></div>
|
| 99 |
+
</div>
|
| 100 |
+
<div class="brush-size">
|
| 101 |
+
<label for="brushSize">Brush Size:</label>
|
| 102 |
+
<input type="range" id="brushSize" min="1" max="50" value="5">
|
| 103 |
+
</div>
|
| 104 |
+
<button id="clearBtn">Clear Canvas</button>
|
| 105 |
+
<div class="ai-tools">
|
| 106 |
+
<button id="aiStyleBtn">AI Style Transfer</button>
|
| 107 |
+
<button id="aiCompleteBtn">AI Auto-Complete</button>
|
| 108 |
+
</div>
|
| 109 |
+
</div>
|
| 110 |
+
<div class="canvas-container">
|
| 111 |
+
<canvas id="drawingCanvas" width="800" height="600"></canvas>
|
| 112 |
+
</div>
|
| 113 |
+
</div>
|
| 114 |
+
|
| 115 |
+
<script>
|
| 116 |
+
const canvas = document.getElementById('drawingCanvas');
|
| 117 |
+
const ctx = canvas.getContext('2d');
|
| 118 |
+
let isDrawing = false;
|
| 119 |
+
let currentTool = 'pencil';
|
| 120 |
+
let currentColor = '#000000';
|
| 121 |
+
let currentSize = 5;
|
| 122 |
+
|
| 123 |
+
canvas.addEventListener('mousedown', startDrawing);
|
| 124 |
+
canvas.addEventListener('mousemove', draw);
|
| 125 |
+
canvas.addEventListener('mouseup', stopDrawing);
|
| 126 |
+
canvas.addEventListener('mouseout', stopDrawing);
|
| 127 |
+
|
| 128 |
+
function startDrawing(e) {
|
| 129 |
+
isDrawing = true;
|
| 130 |
+
draw(e);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
function draw(e) {
|
| 134 |
+
if (!isDrawing) return;
|
| 135 |
+
|
| 136 |
+
ctx.strokeStyle = currentColor;
|
| 137 |
+
ctx.lineWidth = currentSize;
|
| 138 |
+
ctx.lineCap = 'round';
|
| 139 |
+
|
| 140 |
+
ctx.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
|
| 141 |
+
ctx.stroke();
|
| 142 |
+
ctx.beginPath();
|
| 143 |
+
ctx.moveTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
function stopDrawing() {
|
| 147 |
+
isDrawing = false;
|
| 148 |
+
ctx.beginPath();
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
document.getElementById('pencilBtn').addEventListener('click', () => currentTool = 'pencil');
|
| 152 |
+
document.getElementById('brushBtn').addEventListener('click', () => currentTool = 'brush');
|
| 153 |
+
document.getElementById('eraserBtn').addEventListener('click', () => {
|
| 154 |
+
currentTool = 'eraser';
|
| 155 |
+
currentColor = '#ffffff';
|
| 156 |
+
});
|
| 157 |
+
|
| 158 |
+
document.querySelectorAll('.color-swatch').forEach(swatch => {
|
| 159 |
+
swatch.addEventListener('click', (e) => {
|
| 160 |
+
currentColor = e.target.style.backgroundColor;
|
| 161 |
+
});
|
| 162 |
+
});
|
| 163 |
+
|
| 164 |
+
document.getElementById('brushSize').addEventListener('input', (e) => {
|
| 165 |
+
currentSize = e.target.value;
|
| 166 |
+
});
|
| 167 |
+
|
| 168 |
+
document.getElementById('clearBtn').addEventListener('click', () => {
|
| 169 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 170 |
+
});
|
| 171 |
+
|
| 172 |
+
document.getElementById('saveBtn').addEventListener('click', () => {
|
| 173 |
+
const link = document.createElement('a');
|
| 174 |
+
link.download = 'wackcom-masterpiece.png';
|
| 175 |
+
link.href = canvas.toDataURL();
|
| 176 |
+
link.click();
|
| 177 |
+
});
|
| 178 |
+
|
| 179 |
+
document.getElementById('aiStyleBtn').addEventListener('click', () => {
|
| 180 |
+
alert('AI Style Transfer feature coming soon!');
|
| 181 |
+
});
|
| 182 |
+
|
| 183 |
+
document.getElementById('aiCompleteBtn').addEventListener('click', () => {
|
| 184 |
+
alert('AI Auto-Complete feature coming soon!');
|
| 185 |
+
});
|
| 186 |
+
</script>
|
| 187 |
+
</body>
|
| 188 |
+
</html>
|
hbmKkWX8sB2tRrYlW.html
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<title>Wireframe Screams from Beyond</title>
|
| 5 |
+
<base href="simul://3D.resonance.io/chladni-plate/visualizer?show_custom_param=true&add=binaural-wave-generator">
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<style>
|
| 9 |
+
body {
|
| 10 |
+
margin: 0;
|
| 11 |
+
background-color: #000;
|
| 12 |
+
display: flex;
|
| 13 |
+
flex-direction: column;
|
| 14 |
+
align-items: center;
|
| 15 |
+
justify-content: center;
|
| 16 |
+
height: 100vh;
|
| 17 |
+
color: #fff;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
canvas {
|
| 21 |
+
display: block;
|
| 22 |
+
width: 80%;
|
| 23 |
+
height: 80%;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.control-panel {
|
| 27 |
+
position: fixed;
|
| 28 |
+
bottom: 20px;
|
| 29 |
+
left: 20px;
|
| 30 |
+
background: rgba(0, 0, 0, 0.7);
|
| 31 |
+
padding: 10px;
|
| 32 |
+
border-radius: 8px;
|
| 33 |
+
color: #fff;
|
| 34 |
+
font-family: 'Courier New', Courier, monospace;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.control-panel label, .control-panel input {
|
| 38 |
+
margin: 5px;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.visualization-buttons {
|
| 42 |
+
position: fixed;
|
| 43 |
+
top: 50%;
|
| 44 |
+
left: 20px;
|
| 45 |
+
transform: translateY(-50%);
|
| 46 |
+
display: flex;
|
| 47 |
+
flex-direction: column;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.visualization-buttons button {
|
| 51 |
+
background: rgba(0, 0, 0, 0.7);
|
| 52 |
+
color: #fff;
|
| 53 |
+
border: none;
|
| 54 |
+
padding: 10px;
|
| 55 |
+
margin: 5px;
|
| 56 |
+
cursor: pointer;
|
| 57 |
+
font-family: 'Courier New', Courier, monospace;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.visualization-buttons button.active {
|
| 61 |
+
background: rgba(34, 255, 34, 0.7);
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.axis-controls {
|
| 65 |
+
position: fixed;
|
| 66 |
+
top: 20px;
|
| 67 |
+
left: 50%;
|
| 68 |
+
transform: translateX(-50%);
|
| 69 |
+
display: flex;
|
| 70 |
+
background: rgba(0, 0, 0, 0.7);
|
| 71 |
+
padding: 10px;
|
| 72 |
+
border-radius: 8px;
|
| 73 |
+
color: #fff;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.axis-controls div {
|
| 77 |
+
margin: 0 10px;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.axis-controls label {
|
| 81 |
+
display: block;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.color-pickers {
|
| 85 |
+
position: fixed;
|
| 86 |
+
bottom: 20px;
|
| 87 |
+
right: 20px;
|
| 88 |
+
display: flex;
|
| 89 |
+
background: rgba(0, 0, 0, 0.7);
|
| 90 |
+
padding: 10px;
|
| 91 |
+
border-radius: 8px;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.color-pickers input {
|
| 95 |
+
margin: 0 5px;
|
| 96 |
+
width: 40px;
|
| 97 |
+
height: 40px;
|
| 98 |
+
border: none;
|
| 99 |
+
cursor: pointer;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.anaglyph-button {
|
| 103 |
+
position: fixed;
|
| 104 |
+
top: 20px;
|
| 105 |
+
right: 20px;
|
| 106 |
+
background: rgba(0, 0, 0, 0.7);
|
| 107 |
+
color: #fff;
|
| 108 |
+
border: none;
|
| 109 |
+
padding: 10px;
|
| 110 |
+
cursor: pointer;
|
| 111 |
+
font-family: 'Courier New', Courier, monospace;
|
| 112 |
+
}
|
| 113 |
+
</style>
|
| 114 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.135.0/build/three.min.js"></script>
|
| 115 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.135.0/examples/js/controls/OrbitControls.js"></script>
|
| 116 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.135.0/examples/js/effects/AnaglyphEffect.js"></script>
|
| 117 |
+
</head>
|
| 118 |
+
<body>
|
| 119 |
+
<div class="axis-controls">
|
| 120 |
+
<div>
|
| 121 |
+
<label for="x-axis">X:</label>
|
| 122 |
+
<input type="number" id="x-axis" min="-5" max="5" step="0.1" value="0" oninput="updateXAxis(this.value)">
|
| 123 |
+
</div>
|
| 124 |
+
<div>
|
| 125 |
+
<label for="y-axis">Y:</label>
|
| 126 |
+
<input type="number" id="y-axis" min="-5" max="5" step="0.1" value="0" oninput="updateYAxis(this.value)">
|
| 127 |
+
</div>
|
| 128 |
+
<div>
|
| 129 |
+
<label for="z-axis">Z:</label>
|
| 130 |
+
<input type="number" id="z-axis" min="-5" max="5" step="0.1" value="0" oninput="updateZAxis(this.value)">
|
| 131 |
+
</div>
|
| 132 |
+
<div>
|
| 133 |
+
<label for="p-axis">P:</label>
|
| 134 |
+
<input type="number" id="p-axis" min="0" max="10" step="0.1" value="0" oninput="updatePAxis(this.value)">
|
| 135 |
+
</div>
|
| 136 |
+
</div>
|
| 137 |
+
|
| 138 |
+
<div class="control-panel">
|
| 139 |
+
<label for="frequency-slider">Frequency:</label>
|
| 140 |
+
<input type="range" id="frequency-slider" min="1" max="2000" step="1" value="100" oninput="updateFrequency(this.value)">
|
| 141 |
+
<label for="amplitude-slider">Amplitude:</label>
|
| 142 |
+
<input type="range" id="amplitude-slider" min="0" max="10" step="0.1" value="1" oninput="updateAmplitude(this.value)">
|
| 143 |
+
<label for="binaural-frequency">Binaural Beat Frequency:</label>
|
| 144 |
+
<input type="range" id="binaural-frequency" min="1" max="40" step="0.1" value="10" oninput="updateBinauralFrequency(this.value)">
|
| 145 |
+
<label for="color-lfo-frequency">Color LFO Frequency:</label>
|
| 146 |
+
<input type="range" id="color-lfo-frequency" min="0.1" max="10" step="0.1" value="1" oninput="updateColorLFOFrequency(this.value)">
|
| 147 |
+
<label for="volume-slider">Volume:</label>
|
| 148 |
+
<input type="range" id="volume-slider" min="0" max="1" step="0.01" value="0.1" oninput="updateVolume(this.value)">
|
| 149 |
+
</div>
|
| 150 |
+
|
| 151 |
+
<div class="color-pickers">
|
| 152 |
+
<input type="color" id="color1" value="#22ff22" oninput="updateColor1(this.value)">
|
| 153 |
+
<input type="color" id="color2" value="#2222ff" oninput="updateColor2(this.value)">
|
| 154 |
+
<input type="color" id="color3" value="#ff22ff" oninput="updateColor3(this.value)">
|
| 155 |
+
</div>
|
| 156 |
+
|
| 157 |
+
<div class="visualization-buttons">
|
| 158 |
+
<button id="chladni-button" class="active" onclick="setVisualizationMode('chladni')">Chladni</button>
|
| 159 |
+
<button id="ripple-button" onclick="setVisualizationMode('ripple')">Ripple</button>
|
| 160 |
+
<button id="spiral-button" onclick="setVisualizationMode('spiral')">Spiral</button>
|
| 161 |
+
<button id="fractal-button" onclick="setVisualizationMode('fractal')">Fractal</button>
|
| 162 |
+
<button id="vortex-button" onclick="setVisualizationMode('vortex')">Vortex</button>
|
| 163 |
+
<button id="plasma-button" onclick="setVisualizationMode('plasma')">Plasma</button>
|
| 164 |
+
</div>
|
| 165 |
+
|
| 166 |
+
<button class="anaglyph-button" onclick="toggleAnaglyphMode()">Toggle Anaglyph Mode</button>
|
| 167 |
+
|
| 168 |
+
<canvas id="chladniCanvas"></canvas>
|
| 169 |
+
|
| 170 |
+
<script>
|
| 171 |
+
let scene, camera, renderer, chladniSurface, effect;
|
| 172 |
+
let frequency = 100, amplitude = 1, binauralFrequency = 10, colorLFOFrequency = 1;
|
| 173 |
+
let audioContext, binauralOscillator1, binauralOscillator2, gainNode;
|
| 174 |
+
let color1, color2, color3, anaglyphColor1, anaglyphColor2;
|
| 175 |
+
let visualizationMode = 'chladni';
|
| 176 |
+
let xAxis = 0, yAxis = 0, zAxis = 0, pAxis = 0;
|
| 177 |
+
let isAnaglyphMode = false;
|
| 178 |
+
|
| 179 |
+
function resizeCanvas() {
|
| 180 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 181 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
| 182 |
+
camera.updateProjectionMatrix();
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
function initializeAudio() {
|
| 186 |
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
| 187 |
+
gainNode = audioContext.createGain();
|
| 188 |
+
gainNode.gain.value = 0.1;
|
| 189 |
+
|
| 190 |
+
binauralOscillator1 = audioContext.createOscillator();
|
| 191 |
+
binauralOscillator2 = audioContext.createOscillator();
|
| 192 |
+
|
| 193 |
+
binauralOscillator1.frequency.setValueAtTime(200, audioContext.currentTime);
|
| 194 |
+
binauralOscillator2.frequency.setValueAtTime(200 + binauralFrequency, audioContext.currentTime);
|
| 195 |
+
|
| 196 |
+
binauralOscillator1.connect(gainNode).connect(audioContext.destination);
|
| 197 |
+
binauralOscillator2.connect(gainNode).connect(audioContext.destination);
|
| 198 |
+
|
| 199 |
+
binauralOscillator1.start();
|
| 200 |
+
binauralOscillator2.start();
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function updateBinauralFrequency(value) {
|
| 204 |
+
binauralFrequency = parseFloat(value);
|
| 205 |
+
binauralOscillator2.frequency.setValueAtTime(200 + binauralFrequency, audioContext.currentTime);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
function updateColorLFOFrequency(value) {
|
| 209 |
+
colorLFOFrequency = parseFloat(value);
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
function updateVolume(value) {
|
| 213 |
+
gainNode.gain.value = parseFloat(value);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
function init() {
|
| 217 |
+
scene = new THREE.Scene();
|
| 218 |
+
scene.background = new THREE.Color(0x000000);
|
| 219 |
+
|
| 220 |
+
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
| 221 |
+
camera.position.z = 5;
|
| 222 |
+
|
| 223 |
+
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('chladniCanvas') });
|
| 224 |
+
resizeCanvas();
|
| 225 |
+
window.addEventListener('resize', resizeCanvas);
|
| 226 |
+
|
| 227 |
+
effect = new THREE.AnaglyphEffect(renderer);
|
| 228 |
+
effect.setSize(window.innerWidth, window.innerHeight);
|
| 229 |
+
|
| 230 |
+
let controls = new THREE.OrbitControls(camera, renderer.domElement);
|
| 231 |
+
|
| 232 |
+
color1 = new THREE.Color(0x22ff22);
|
| 233 |
+
color2 = new THREE.Color(0x2222ff);
|
| 234 |
+
color3 = new THREE.Color(0xff22ff);
|
| 235 |
+
|
| 236 |
+
anaglyphColor1 = new THREE.Color(0x00ffff); // cyan
|
| 237 |
+
anaglyphColor2 = new THREE.Color(0xff0000); // red
|
| 238 |
+
|
| 239 |
+
createChladniSurface();
|
| 240 |
+
initializeAudio();
|
| 241 |
+
|
| 242 |
+
function animate() {
|
| 243 |
+
requestAnimationFrame(animate);
|
| 244 |
+
if (!isAnaglyphMode) updateVisualization();
|
| 245 |
+
else updateAnaglyphVisualization();
|
| 246 |
+
if (isAnaglyphMode) {
|
| 247 |
+
effect.render(scene, camera);
|
| 248 |
+
} else {
|
| 249 |
+
renderer.render(scene, camera);
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
animate();
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
function createChladniSurface() {
|
| 257 |
+
let geometry = new THREE.PlaneGeometry(5, 5, 100, 100);
|
| 258 |
+
let material = new THREE.MeshBasicMaterial({ color: 0x22ff22, wireframe: true });
|
| 259 |
+
|
| 260 |
+
chladniSurface = new THREE.Mesh(geometry, material);
|
| 261 |
+
scene.add(chladniSurface);
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
function updateVisualization() {
|
| 265 |
+
let positions = chladniSurface.geometry.attributes.position.array;
|
| 266 |
+
|
| 267 |
+
let t = Date.now() / 1000;
|
| 268 |
+
let lerpFactor = (Math.sin(t * colorLFOFrequency) + 1) / 2;
|
| 269 |
+
let currentColor = new THREE.Color().lerpColors(color1, color2, lerpFactor).lerpHSL(color3, lerpFactor);
|
| 270 |
+
chladniSurface.material.color = currentColor;
|
| 271 |
+
|
| 272 |
+
for (let i = 0; i < positions.length; i += 3) {
|
| 273 |
+
let x = positions[i] + xAxis;
|
| 274 |
+
let y = positions[i + 1] + yAxis;
|
| 275 |
+
let z;
|
| 276 |
+
|
| 277 |
+
if (visualizationMode === 'chladni') {
|
| 278 |
+
z = Math.sin(Math.sqrt((x * x + y * y) * (frequency + binauralFrequency)) - t) * amplitude + zAxis;
|
| 279 |
+
} else if (visualizationMode === 'ripple') {
|
| 280 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 281 |
+
z = Math.sin((distance - t * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 282 |
+
} else if (visualizationMode === 'spiral') {
|
| 283 |
+
let angle = Math.atan2(y, x);
|
| 284 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 285 |
+
z = Math.sin((distance - t * 5 + angle * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 286 |
+
} else if (visualizationMode === 'fractal') {
|
| 287 |
+
let angle = Math.atan2(y, x);
|
| 288 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 289 |
+
z = Math.sin(distance * Math.log(distance) * (frequency + binauralFrequency) - t) * amplitude + zAxis;
|
| 290 |
+
} else if (visualizationMode === 'vortex') {
|
| 291 |
+
let angle = Math.atan2(y, x);
|
| 292 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 293 |
+
z = Math.sin((angle * 10 - distance * 2 - t * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 294 |
+
} else if (visualizationMode === 'plasma') {
|
| 295 |
+
z = Math.sin((x + t) * (frequency + binauralFrequency) / 50)
|
| 296 |
+
+ Math.sin((y + t) * (frequency + binauralFrequency) / 50) * amplitude + zAxis;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
z += Math.sin(t * pAxis) * 0.1;
|
| 300 |
+
|
| 301 |
+
positions[i + 2] = z;
|
| 302 |
+
}
|
| 303 |
+
chladniSurface.geometry.attributes.position.needsUpdate = true;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
function updateAnaglyphVisualization() {
|
| 307 |
+
let positions = chladniSurface.geometry.attributes.position.array;
|
| 308 |
+
|
| 309 |
+
let currentColor = anaglyphColor1;
|
| 310 |
+
chladniSurface.material.color = currentColor;
|
| 311 |
+
|
| 312 |
+
for (let i = 0; i < positions.length; i += 3) {
|
| 313 |
+
let x = positions[i] + xAxis;
|
| 314 |
+
let y = positions[i + 1] + yAxis;
|
| 315 |
+
let z;
|
| 316 |
+
|
| 317 |
+
if (visualizationMode === 'chladni') {
|
| 318 |
+
z = Math.sin(Math.sqrt((x * x + y * y) * (frequency + binauralFrequency)) - (Date.now() / 1000)) * amplitude + zAxis;
|
| 319 |
+
} else if (visualizationMode === 'ripple') {
|
| 320 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 321 |
+
z = Math.sin((distance - (Date.now() / 1000) * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 322 |
+
} else if (visualizationMode === 'spiral') {
|
| 323 |
+
let angle = Math.atan2(y, x);
|
| 324 |
+
let distance = Math.sqrt(x * x + y * y); z = Math.sin((distance - (Date.now() / 1000) * 5 + angle * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 325 |
+
} else if (visualizationMode === 'fractal') {
|
| 326 |
+
let angle = Math.atan2(y, x);
|
| 327 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 328 |
+
z = Math.sin(distance * Math.log(distance) * (frequency + binauralFrequency) - (Date.now() / 1000)) * amplitude + zAxis;
|
| 329 |
+
} else if (visualizationMode === 'vortex') {
|
| 330 |
+
let angle = Math.atan2(y, x);
|
| 331 |
+
let distance = Math.sqrt(x * x + y * y);
|
| 332 |
+
z = Math.sin((angle * 10 - distance * 2 - (Date.now() / 1000) * 5) * (frequency + binauralFrequency) / 100) * amplitude + zAxis;
|
| 333 |
+
} else if (visualizationMode === 'plasma') {
|
| 334 |
+
z = Math.sin((x + (Date.now() / 1000)) * (frequency + binauralFrequency) / 50)
|
| 335 |
+
+ Math.sin((y + (Date.now() / 1000)) * (frequency + binauralFrequency) / 50) * amplitude + zAxis;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
z += Math.sin((Date.now() / 1000) * pAxis) * 0.1;
|
| 339 |
+
|
| 340 |
+
positions[i + 2] = z;
|
| 341 |
+
}
|
| 342 |
+
chladniSurface.geometry.attributes.position.needsUpdate = true;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
function updateFrequency(value) {
|
| 346 |
+
frequency = parseFloat(value);
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
function updateAmplitude(value) {
|
| 350 |
+
amplitude = parseFloat(value);
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
function setVisualizationMode(mode) {
|
| 354 |
+
visualizationMode = mode;
|
| 355 |
+
document.querySelectorAll('.visualization-buttons button').forEach(button => {
|
| 356 |
+
button.classList.toggle('active', button.id === `${mode}-button`);
|
| 357 |
+
});
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
function updateColor1(value) {
|
| 361 |
+
color1.set(value);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
function updateColor2(value) {
|
| 365 |
+
color2.set(value);
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
function updateColor3(value) {
|
| 369 |
+
color3.set(value);
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
function updateXAxis(value) {
|
| 373 |
+
xAxis = parseFloat(value);
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
function updateYAxis(value) {
|
| 377 |
+
yAxis = parseFloat(value);
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
function updateZAxis(value) {
|
| 381 |
+
zAxis = parseFloat(value);
|
| 382 |
+
}
|
| 383 |
+
|
| 384 |
+
function updatePAxis(value) {
|
| 385 |
+
pAxis = parseFloat(value);
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
function toggleAnaglyphMode() {
|
| 389 |
+
isAnaglyphMode = !isAnaglyphMode;
|
| 390 |
+
document.getElementById('color1').disabled = isAnaglyphMode;
|
| 391 |
+
document.getElementById('color2').disabled = isAnaglyphMode;
|
| 392 |
+
document.getElementById('color3').disabled = isAnaglyphMode;
|
| 393 |
+
document.getElementById('color-lfo-frequency').disabled = isAnaglyphMode;
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
window.onload = init;
|
| 397 |
+
</script>
|
| 398 |
+
</body>
|
| 399 |
+
</html>
|
jDnGYMx9M566RB31a.html
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://piano.midi/webmidi@latest/flex-horizontal/use-event.note.name+event.note.octave__not.value/in_getKeys_just_do_straight_comparison/whiteKeys_and_blackKeys_are_nodeLists_not_arrays/position_fix_keyboard_above_text/playable-with-mouse-too/hide<comments>and<links>/drop_piano_2_octaves/abnormify_sound/push_audio_creativity/explore_tonejs_instruments/drop_piano_decibels_by_10/add_higher_octave/hush_comments"><title>Sonic Artistry</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
margin: 0;
|
| 5 |
+
padding: 0;
|
| 6 |
+
background: radial-gradient(circle at center, #111, #333);
|
| 7 |
+
color: #fff;
|
| 8 |
+
font-family: monospace;
|
| 9 |
+
overflow: hidden;
|
| 10 |
+
display: flex;
|
| 11 |
+
flex-direction: column;
|
| 12 |
+
justify-content: center;
|
| 13 |
+
align-items: center;
|
| 14 |
+
height: 100vh;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
#instrumentSelector {
|
| 18 |
+
position: absolute;
|
| 19 |
+
top: 10px;
|
| 20 |
+
left: 10px;
|
| 21 |
+
background-color: rgba(255, 255, 255, 0.1);
|
| 22 |
+
padding: 10px;
|
| 23 |
+
border-radius: 5px;
|
| 24 |
+
z-index: 1;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
#keyboardContainer {
|
| 28 |
+
display: flex;
|
| 29 |
+
flex-direction: row;
|
| 30 |
+
align-items: flex-end;
|
| 31 |
+
margin-bottom: 20px;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.key {
|
| 35 |
+
width: 50px;
|
| 36 |
+
height: 200px;
|
| 37 |
+
background: linear-gradient(to bottom, #eee, #ccc);
|
| 38 |
+
border: 1px solid #000;
|
| 39 |
+
box-sizing: border-box;
|
| 40 |
+
cursor: pointer;
|
| 41 |
+
transition: background-color 0.2s, transform 0.2s;
|
| 42 |
+
position: relative;
|
| 43 |
+
transform-style: preserve-3d;
|
| 44 |
+
display: flex;
|
| 45 |
+
justify-content: center;
|
| 46 |
+
align-items: flex-end;
|
| 47 |
+
padding-bottom: 10px;
|
| 48 |
+
font-size: 12px;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.key::after {
|
| 52 |
+
content: attr(data-note);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.key.black {
|
| 56 |
+
width: 30px;
|
| 57 |
+
height: 120px;
|
| 58 |
+
background: linear-gradient(to bottom, #555, #222);
|
| 59 |
+
margin-right: -15px;
|
| 60 |
+
margin-left: -15px;
|
| 61 |
+
z-index: 1;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.key:hover {
|
| 65 |
+
background: linear-gradient(to bottom, #ddd, #bbb);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
.key.black:hover {
|
| 69 |
+
background: linear-gradient(to bottom, #777, #444);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.key.pressed {
|
| 73 |
+
background: linear-gradient(to bottom, #ccc, #aaa);
|
| 74 |
+
transform: translateZ(20px);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.key.black.pressed {
|
| 78 |
+
background: linear-gradient(to bottom, #666, #333);
|
| 79 |
+
transform: translateZ(20px);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.key::before {
|
| 83 |
+
content: "";
|
| 84 |
+
position: absolute;
|
| 85 |
+
top: 50%;
|
| 86 |
+
left: 50%;
|
| 87 |
+
transform: translate(-50%, -50%);
|
| 88 |
+
width: 0;
|
| 89 |
+
height: 0;
|
| 90 |
+
background-color: rgba(255, 255, 255, 0.5);
|
| 91 |
+
border-radius: 50%;
|
| 92 |
+
opacity: 0;
|
| 93 |
+
transition: width 0.2s, height 0.2s, opacity 0.2s;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.key.pressed::before {
|
| 97 |
+
width: 100px;
|
| 98 |
+
height: 100px;
|
| 99 |
+
opacity: 1;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.key.black::before {
|
| 103 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 104 |
+
}
|
| 105 |
+
</style>
|
| 106 |
+
</head>
|
| 107 |
+
<body>
|
| 108 |
+
<div id="instrumentSelector">
|
| 109 |
+
<label for="instrumentSelect">Select Instrument:</label>
|
| 110 |
+
<select id="instrumentSelect">
|
| 111 |
+
<option value="AMSynth">AM Synth</option>
|
| 112 |
+
<option value="FMSynth">FM Synth</option>
|
| 113 |
+
<option value="DuoSynth">Duo Synth</option>
|
| 114 |
+
<option value="MonoSynth">Mono Synth</option>
|
| 115 |
+
<option value="PluckSynth">Pluck Synth</option>
|
| 116 |
+
<option value="Membrane">Membrane Synth</option>
|
| 117 |
+
<option value="MetalSynth">Metal Synth</option>
|
| 118 |
+
<option value="NoiseSynth">Noise Synth</option>
|
| 119 |
+
</select>
|
| 120 |
+
</div>
|
| 121 |
+
<div id="keyboardContainer">
|
| 122 |
+
<div class="key white" data-note="C2" data-midi="36"></div>
|
| 123 |
+
<div class="key black" data-note="C#2" data-midi="37"></div>
|
| 124 |
+
<div class="key white" data-note="D2" data-midi="38"></div>
|
| 125 |
+
<div class="key black" data-note="D#2" data-midi="39"></div>
|
| 126 |
+
<div class="key white" data-note="E2" data-midi="40"></div>
|
| 127 |
+
<div class="key white" data-note="F2" data-midi="41"></div>
|
| 128 |
+
<div class="key black" data-note="F#2" data-midi="42"></div>
|
| 129 |
+
<div class="key white" data-note="G2" data-midi="43"></div>
|
| 130 |
+
<div class="key black" data-note="G#2" data-midi="44"></div>
|
| 131 |
+
<div class="key white" data-note="A2" data-midi="45"></div>
|
| 132 |
+
<div class="key black" data-note="A#2" data-midi="46"></div>
|
| 133 |
+
<div class="key white" data-note="B2" data-midi="47"></div>
|
| 134 |
+
<div class="key white" data-note="C3" data-midi="48"></div>
|
| 135 |
+
<div class="key black" data-note="C#3" data-midi="49"></div>
|
| 136 |
+
<div class="key white" data-note="D3" data-midi="50"></div>
|
| 137 |
+
<div class="key black" data-note="D#3" data-midi="51"></div>
|
| 138 |
+
<div class="key white" data-note="E3" data-midi="52"></div>
|
| 139 |
+
<div class="key white" data-note="F3" data-midi="53"></div>
|
| 140 |
+
<div class="key black" data-note="F#3" data-midi="54"></div>
|
| 141 |
+
<div class="key white" data-note="G3" data-midi="55"></div>
|
| 142 |
+
<div class="key black" data-note="G#3" data-midi="56"></div>
|
| 143 |
+
<div class="key white" data-note="A3" data-midi="57"></div>
|
| 144 |
+
<div class="key black" data-note="A#3" data-midi="58"></div>
|
| 145 |
+
<div class="key white" data-note="B3" data-midi="59"></div>
|
| 146 |
+
<div class="key white" data-note="C4" data-midi="60"></div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<script src="https://cdn.jsdelivr.net/npm/tone@14.7.77/build/Tone.js"></script>
|
| 150 |
+
<script src="https://cdn.jsdelivr.net/npm/webmidi@latest/dist/iife/webmidi.iife.min.js"></script>
|
| 151 |
+
<script>
|
| 152 |
+
const whiteKeys = document.querySelectorAll('.key.white');
|
| 153 |
+
const blackKeys = document.querySelectorAll('.key.black');
|
| 154 |
+
const instrumentSelect = document.getElementById('instrumentSelect');
|
| 155 |
+
let synth;
|
| 156 |
+
const reverb = new Tone.Reverb({ decay: 5, wet: 0.5 }).toDestination();
|
| 157 |
+
const delay = new Tone.FeedbackDelay({ delayTime: 0.5, feedback: 0.25, wet: 0.25 }).toDestination();
|
| 158 |
+
|
| 159 |
+
function initSynth(instrumentType) {
|
| 160 |
+
if (synth) {
|
| 161 |
+
synth.disconnect();
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
switch (instrumentType) {
|
| 165 |
+
case 'AMSynth':
|
| 166 |
+
synth = new Tone.PolySynth(Tone.AMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 167 |
+
break;
|
| 168 |
+
case 'FMSynth':
|
| 169 |
+
synth = new Tone.PolySynth(Tone.FMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 170 |
+
break;
|
| 171 |
+
case 'DuoSynth':
|
| 172 |
+
synth = new Tone.PolySynth(Tone.DuoSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 173 |
+
break;
|
| 174 |
+
case 'MonoSynth':
|
| 175 |
+
synth = new Tone.MonoSynth({ volume: -10 }).toDestination();
|
| 176 |
+
break;
|
| 177 |
+
case 'PluckSynth':
|
| 178 |
+
synth = new Tone.PluckSynth({ volume: -10 }).toDestination();
|
| 179 |
+
break;
|
| 180 |
+
case 'Membrane':
|
| 181 |
+
synth = new Tone.PolySynth(Tone.MembraneSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 182 |
+
break;
|
| 183 |
+
case 'MetalSynth':
|
| 184 |
+
synth = new Tone.PolySynth(Tone.MetalSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 185 |
+
break;
|
| 186 |
+
case 'NoiseSynth':
|
| 187 |
+
synth = new Tone.NoiseSynth({ volume: -10 }).toDestination();
|
| 188 |
+
break;
|
| 189 |
+
default:
|
| 190 |
+
synth = new Tone.PolySynth(Tone.AMSynth, { polyphony: 32, volume: -10 }).toDestination();
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
synth.connect(reverb);
|
| 194 |
+
synth.connect(delay);
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
initSynth(instrumentSelect.value);
|
| 198 |
+
|
| 199 |
+
instrumentSelect.addEventListener('change', (event) => {
|
| 200 |
+
initSynth(event.target.value);
|
| 201 |
+
});
|
| 202 |
+
|
| 203 |
+
let midiAccess, midiInput;
|
| 204 |
+
|
| 205 |
+
WebMidi.enable(err => {
|
| 206 |
+
if (err) {
|
| 207 |
+
console.error('Failed to enable WebMIDI:', err);
|
| 208 |
+
return;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
console.log('WebMIDI enabled!');
|
| 212 |
+
midiInput = WebMidi.inputs[0];
|
| 213 |
+
|
| 214 |
+
if (!midiInput) {
|
| 215 |
+
console.warn('No MIDI input device detected.');
|
| 216 |
+
return;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
console.log(`Connected to MIDI input device: ${midiInput.name}`);
|
| 220 |
+
|
| 221 |
+
midiInput.addListener('noteon', 'all', event => {
|
| 222 |
+
const midiNoteValue = `${event.note.name}${event.note.octave}`;
|
| 223 |
+
handleNoteOn(midiNoteValue, event.velocity / 127);
|
| 224 |
+
});
|
| 225 |
+
|
| 226 |
+
midiInput.addListener('noteoff', 'all', event => {
|
| 227 |
+
const midiNoteValue = `${event.note.name}${event.note.octave}`;
|
| 228 |
+
handleNoteOff(midiNoteValue);
|
| 229 |
+
});
|
| 230 |
+
});
|
| 231 |
+
|
| 232 |
+
function handleNoteOn(midiNoteValue, velocity) {
|
| 233 |
+
const whiteKey = Array.from(whiteKeys).find(key => key.dataset.note === midiNoteValue);
|
| 234 |
+
const blackKey = Array.from(blackKeys).find(key => key.dataset.note === midiNoteValue);
|
| 235 |
+
|
| 236 |
+
if (whiteKey) {
|
| 237 |
+
whiteKey.classList.add('pressed');
|
| 238 |
+
synth.triggerAttack(noteToFrequency(whiteKey.dataset.midi), velocity);
|
| 239 |
+
} else if (blackKey) {
|
| 240 |
+
blackKey.classList.add('pressed');
|
| 241 |
+
synth.triggerAttack(noteToFrequency(blackKey.dataset.midi), velocity);
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
function handleNoteOff(midiNoteValue) {
|
| 246 |
+
const whiteKey = Array.from(whiteKeys).find(key => key.dataset.note === midiNoteValue);
|
| 247 |
+
const blackKey = Array.from(blackKeys).find(key => key.dataset.note === midiNoteValue);
|
| 248 |
+
|
| 249 |
+
if (whiteKey) {
|
| 250 |
+
whiteKey.classList.remove('pressed');
|
| 251 |
+
synth.triggerRelease(noteToFrequency(whiteKey.dataset.midi));
|
| 252 |
+
} else if (blackKey) {
|
| 253 |
+
blackKey.classList.remove('pressed');
|
| 254 |
+
synth.triggerRelease(noteToFrequency(blackKey.dataset.midi));
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
function whiteKeyMouseDown(event) {
|
| 259 |
+
const key = event.target;
|
| 260 |
+
key.classList.add('pressed');
|
| 261 |
+
synth.triggerAttack(noteToFrequency(key.dataset.midi), 0.5);
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
function whiteKeyMouseUp(event) {
|
| 265 |
+
const key = event.target;
|
| 266 |
+
key.classList.remove('pressed');
|
| 267 |
+
synth.triggerRelease(noteToFrequency(key.dataset.midi));
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
function blackKeyMouseDown(event) {
|
| 271 |
+
const key = event.target;
|
| 272 |
+
key.classList.add('pressed');
|
| 273 |
+
synth.triggerAttack(noteToFrequency(key.dataset.midi), 0.5);
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
function blackKeyMouseUp(event) {
|
| 277 |
+
const key = event.target;
|
| 278 |
+
key.classList.remove('pressed');
|
| 279 |
+
synth.triggerRelease(noteToFrequency(key.dataset.midi));
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
whiteKeys.forEach(key => {
|
| 283 |
+
key.addEventListener('mousedown', whiteKeyMouseDown);
|
| 284 |
+
key.addEventListener('mouseup', whiteKeyMouseUp);
|
| 285 |
+
key.addEventListener('mouseleave', whiteKeyMouseUp);
|
| 286 |
+
});
|
| 287 |
+
|
| 288 |
+
blackKeys.forEach(key => {
|
| 289 |
+
key.addEventListener('mousedown', blackKeyMouseDown);
|
| 290 |
+
key.addEventListener('mouseup', blackKeyMouseUp);
|
| 291 |
+
key.addEventListener('mouseleave', blackKeyMouseUp);
|
| 292 |
+
});
|
| 293 |
+
|
| 294 |
+
function noteToFrequency(note) {
|
| 295 |
+
const baseFrequency = 440;
|
| 296 |
+
return baseFrequency * Math.pow(2, (note - 69) / 12);
|
| 297 |
+
}
|
| 298 |
+
</script>
|
| 299 |
+
</body>
|
| 300 |
+
</html>
|
mIvBgUJS6Lekllndg.html
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="websim://surreal-sensation/jimson-weed-hallucination?world-distortion=true&kaleidoscope-vision=true&ambient-sound=warped&shifting-sensations=extreme&real-unreal=blurred&return-to-reality=gradual"><title>Waveform Sim</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
background: black;
|
| 5 |
+
color: #e8f3ef;
|
| 6 |
+
font-family: 'Lucida Handwriting', cursive;
|
| 7 |
+
text-shadow: 0 0 10px #00FF00;
|
| 8 |
+
margin: 0;
|
| 9 |
+
height: 100vh;
|
| 10 |
+
display: flex;
|
| 11 |
+
justify-content: center;
|
| 12 |
+
align-items: center;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
#dreamscape {
|
| 16 |
+
position: absolute;
|
| 17 |
+
width: 100%;
|
| 18 |
+
height: 100%;
|
| 19 |
+
z-index: -1;
|
| 20 |
+
filter: hue-rotate(180deg);
|
| 21 |
+
opacity: 0.8;
|
| 22 |
+
background-size: cover;
|
| 23 |
+
background-repeat: repeat;
|
| 24 |
+
animation: swirl 5s linear infinite, morph 10s ease-in-out infinite alternate, warp 15s linear infinite;
|
| 25 |
+
transform: perspective(500px) scale(1);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
@keyframes swirl {
|
| 29 |
+
0% {
|
| 30 |
+
transform: rotate(0deg);
|
| 31 |
+
}
|
| 32 |
+
100% {
|
| 33 |
+
transform: rotate(360deg);
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
@keyframes morph {
|
| 38 |
+
0% {
|
| 39 |
+
border-radius: 30% 70% 70% 30% / 50% 30% 70% 50%;
|
| 40 |
+
}
|
| 41 |
+
100% {
|
| 42 |
+
border-radius: 64% 36% 35% 65% / 34% 62% 38% 66%;
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
@keyframes warp {
|
| 47 |
+
0% {
|
| 48 |
+
transform: perspective(500px) scale(1) rotateX(5deg);
|
| 49 |
+
}
|
| 50 |
+
50% {
|
| 51 |
+
transform: perspective(500px) scale(1.2) rotateX(-5deg);
|
| 52 |
+
}
|
| 53 |
+
100% {
|
| 54 |
+
transform: perspective(500px) scale(0.8) rotateX(5deg);
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
#controls {
|
| 59 |
+
position: absolute;
|
| 60 |
+
bottom: 0;
|
| 61 |
+
background: rgba(0,0,0,0.8);
|
| 62 |
+
padding: 10px;
|
| 63 |
+
width: 100%;
|
| 64 |
+
display: flex;
|
| 65 |
+
flex-wrap: wrap;
|
| 66 |
+
justify-content: center;
|
| 67 |
+
align-items: center;
|
| 68 |
+
z-index: 1;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
button {
|
| 72 |
+
background: #000;
|
| 73 |
+
color: #0f0;
|
| 74 |
+
border: 1px solid #0f0;
|
| 75 |
+
padding: 5px 10px;
|
| 76 |
+
margin: 5px;
|
| 77 |
+
text-transform: uppercase;
|
| 78 |
+
cursor: pointer;
|
| 79 |
+
outline: none;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
button:hover {
|
| 83 |
+
background: #0f0;
|
| 84 |
+
color: #000;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
input[type=range] {
|
| 88 |
+
-webkit-appearance: none;
|
| 89 |
+
margin: 5px;
|
| 90 |
+
width: 120px;
|
| 91 |
+
display: inline-block;
|
| 92 |
+
vertical-align: middle;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
input[type=range]:focus {
|
| 96 |
+
outline: none;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
input[type=range]::-webkit-slider-runnable-track {
|
| 100 |
+
width: 100%;
|
| 101 |
+
height: 3px;
|
| 102 |
+
cursor: pointer;
|
| 103 |
+
background: #008080;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
input[type=range]::-webkit-slider-thumb {
|
| 107 |
+
height: 15px;
|
| 108 |
+
width: 15px;
|
| 109 |
+
border-radius: 50%;
|
| 110 |
+
background: #40E0D0;
|
| 111 |
+
cursor: pointer;
|
| 112 |
+
-webkit-appearance: none;
|
| 113 |
+
margin-top: -6px;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
input[type=range]:focus::-webkit-slider-runnable-track {
|
| 117 |
+
background: #008080;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
.param {
|
| 121 |
+
display: inline-block;
|
| 122 |
+
margin: 5px;
|
| 123 |
+
vertical-align: middle;
|
| 124 |
+
text-align: center;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
.param span {
|
| 128 |
+
display: block;
|
| 129 |
+
color: #40E0D0;
|
| 130 |
+
font-size: 12px;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
.param-value {
|
| 134 |
+
display: inline-block;
|
| 135 |
+
width: 40px;
|
| 136 |
+
text-align: center;
|
| 137 |
+
font-size: 12px;
|
| 138 |
+
margin-left: 5px;
|
| 139 |
+
vertical-align: middle;
|
| 140 |
+
color: #ff0;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
#oscilloscope-container {
|
| 144 |
+
position: absolute;
|
| 145 |
+
top: 0;
|
| 146 |
+
left: 0;
|
| 147 |
+
right: 0;
|
| 148 |
+
bottom: 120px;
|
| 149 |
+
border: 1px solid #008080;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
#oscilloscope {
|
| 153 |
+
width: 100%;
|
| 154 |
+
height: 100%;
|
| 155 |
+
}
|
| 156 |
+
</style>
|
| 157 |
+
|
| 158 |
+
<script>
|
| 159 |
+
let audioCtx, oscillator, gainNode, distortion, reverb, biquadFilter, lfo;
|
| 160 |
+
let analyser, oscilloscopeCtx;
|
| 161 |
+
|
| 162 |
+
const dreamscape = document.getElementById('dreamscape');
|
| 163 |
+
|
| 164 |
+
function startAudio() {
|
| 165 |
+
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
| 166 |
+
|
| 167 |
+
oscillator = audioCtx.createOscillator();
|
| 168 |
+
gainNode = audioCtx.createGain();
|
| 169 |
+
lfo = audioCtx.createOscillator();
|
| 170 |
+
|
| 171 |
+
oscillator.connect(gainNode);
|
| 172 |
+
lfo.connect(gainNode.gain);
|
| 173 |
+
|
| 174 |
+
distortion = audioCtx.createWaveShaper();
|
| 175 |
+
distortion.oversample = '4x';
|
| 176 |
+
gainNode.connect(distortion);
|
| 177 |
+
|
| 178 |
+
reverb = audioCtx.createConvolver();
|
| 179 |
+
distortion.connect(reverb);
|
| 180 |
+
|
| 181 |
+
biquadFilter = audioCtx.createBiquadFilter();
|
| 182 |
+
reverb.connect(biquadFilter).connect(audioCtx.destination);
|
| 183 |
+
|
| 184 |
+
oscilloscopeCtx = oscilloscope.getContext('2d');
|
| 185 |
+
analyser = audioCtx.createAnalyser();
|
| 186 |
+
biquadFilter.connect(analyser);
|
| 187 |
+
|
| 188 |
+
oscillator.start();
|
| 189 |
+
lfo.start();
|
| 190 |
+
updateParams();
|
| 191 |
+
renderOscilloscope();
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
function stopAudio() {
|
| 195 |
+
oscillator.stop();
|
| 196 |
+
lfo.stop();
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
function changeWaveType(type) {
|
| 200 |
+
oscillator.type = type;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function updateParams() {
|
| 204 |
+
oscillator.frequency.setValueAtTime(freq.value, audioCtx.currentTime);
|
| 205 |
+
oscillator.detune.setValueAtTime(detune.value, audioCtx.currentTime);
|
| 206 |
+
gainNode.gain.setValueAtTime(gain.value, audioCtx.currentTime);
|
| 207 |
+
distortion.curve = makeDistortionCurve(distortionAmount.value);
|
| 208 |
+
distortion.oversample = oversample.value;
|
| 209 |
+
reverb.buffer = impulseResponse(reverbTime.value, reverbDecay.value);
|
| 210 |
+
biquadFilter.type = filterType.value;
|
| 211 |
+
biquadFilter.frequency.setValueAtTime(filterFreq.value, audioCtx.currentTime);
|
| 212 |
+
biquadFilter.Q.setValueAtTime(filterQ.value, audioCtx.currentTime);
|
| 213 |
+
lfo.type = lfoType.value;
|
| 214 |
+
lfo.frequency.setValueAtTime(lfoFreq.value, audioCtx.currentTime);
|
| 215 |
+
|
| 216 |
+
freqValue.textContent = freq.value + ' Hz';
|
| 217 |
+
detuneValue.textContent = detune.value + ' cents';
|
| 218 |
+
gainValue.textContent = gain.value;
|
| 219 |
+
distortionValue.textContent = distortionAmount.value;
|
| 220 |
+
oversampleValue.textContent = oversample.value;
|
| 221 |
+
reverbTimeValue.textContent = reverbTime.value + ' s';
|
| 222 |
+
reverbDecayValue.textContent = reverbDecay.value;
|
| 223 |
+
filterFreqValue.textContent = filterFreq.value + ' Hz';
|
| 224 |
+
filterQValue.textContent = filterQ.value;
|
| 225 |
+
lfoFreqValue.textContent = lfoFreq.value + ' Hz';
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
function impulseResponse(duration, decay) {
|
| 229 |
+
const sampleRate = audioCtx.sampleRate;
|
| 230 |
+
const length = sampleRate * duration;
|
| 231 |
+
const impulse = audioCtx.createBuffer(2, length, sampleRate);
|
| 232 |
+
const impulseL = impulse.getChannelData(0);
|
| 233 |
+
const impulseR = impulse.getChannelData(1);
|
| 234 |
+
|
| 235 |
+
for (let i = 0; i < length; i++) {
|
| 236 |
+
impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, decay);
|
| 237 |
+
impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, decay);
|
| 238 |
+
}
|
| 239 |
+
return impulse;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
function makeDistortionCurve(amount) {
|
| 243 |
+
const n_samples = 44100;
|
| 244 |
+
const curve = new Float32Array(n_samples);
|
| 245 |
+
const deg = Math.PI / 180;
|
| 246 |
+
|
| 247 |
+
for (let i = 0 ; i < n_samples; ++i ) {
|
| 248 |
+
let x = i * 2 / n_samples - 1;
|
| 249 |
+
curve[i] = (3 + amount) * x * 20 * deg / (Math.PI + amount * Math.abs(x));
|
| 250 |
+
}
|
| 251 |
+
return curve;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
function changeBgGradient(startColor, endColor) {
|
| 255 |
+
dreamscape.style.backgroundImage = `linear-gradient(45deg, ${startColor}, ${endColor})`;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
function renderOscilloscope() {
|
| 259 |
+
requestAnimationFrame(renderOscilloscope);
|
| 260 |
+
|
| 261 |
+
const bufferLength = analyser.frequencyBinCount;
|
| 262 |
+
const dataArray = new Uint8Array(bufferLength);
|
| 263 |
+
analyser.getByteTimeDomainData(dataArray);
|
| 264 |
+
|
| 265 |
+
oscilloscopeCtx.fillStyle = 'rgb(0, 0, 0)';
|
| 266 |
+
oscilloscopeCtx.fillRect(0, 0, oscilloscope.width, oscilloscope.height);
|
| 267 |
+
oscilloscopeCtx.lineWidth = 2;
|
| 268 |
+
oscilloscopeCtx.strokeStyle = 'rgb(0, 255, 0)';
|
| 269 |
+
oscilloscopeCtx.beginPath();
|
| 270 |
+
|
| 271 |
+
const sliceWidth = oscilloscope.width * 1.0 / bufferLength;
|
| 272 |
+
let x = 0;
|
| 273 |
+
|
| 274 |
+
for (let i = 0; i < bufferLength; i++) {
|
| 275 |
+
const v = dataArray[i] / 128.0;
|
| 276 |
+
const y = v * oscilloscope.height/2;
|
| 277 |
+
|
| 278 |
+
if (i === 0) {
|
| 279 |
+
oscilloscopeCtx.moveTo(x, y);
|
| 280 |
+
} else {
|
| 281 |
+
oscilloscopeCtx.lineTo(x, y);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
x += sliceWidth;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
oscilloscopeCtx.lineTo(oscilloscope.width, oscilloscope.height/2);
|
| 288 |
+
oscilloscopeCtx.stroke();
|
| 289 |
+
}
|
| 290 |
+
</script>
|
| 291 |
+
</head>
|
| 292 |
+
<body>
|
| 293 |
+
<div id="dreamscape"></div>
|
| 294 |
+
|
| 295 |
+
<div id="oscilloscope-container">
|
| 296 |
+
<canvas id="oscilloscope"></canvas>
|
| 297 |
+
</div>
|
| 298 |
+
|
| 299 |
+
<div id="controls">
|
| 300 |
+
<button id="start" onclick="startAudio()">Start</button>
|
| 301 |
+
<button id="stop" onclick="stopAudio()">Stop</button>
|
| 302 |
+
|
| 303 |
+
<select id="waveType" onchange="changeWaveType(this.value)">
|
| 304 |
+
<option value="sine">Sine</option>
|
| 305 |
+
<option value="square">Square</option>
|
| 306 |
+
<option value="sawtooth">Sawtooth</option>
|
| 307 |
+
<option value="triangle">Triangle</option>
|
| 308 |
+
</select>
|
| 309 |
+
|
| 310 |
+
<div class="param">
|
| 311 |
+
<input id="freq" type="range" min="20" max="1500" value="110" step="1" oninput="updateParams()">
|
| 312 |
+
<span>Frequency</span>
|
| 313 |
+
<span id="freqValue" class="param-value">110 Hz</span>
|
| 314 |
+
</div>
|
| 315 |
+
|
| 316 |
+
<div class="param">
|
| 317 |
+
<input id="detune" type="range" min="-100" max="100" value="0" step="1" oninput="updateParams()">
|
| 318 |
+
<span>Detune</span>
|
| 319 |
+
<span id="detuneValue" class="param-value">0 cents</span>
|
| 320 |
+
</div>
|
| 321 |
+
|
| 322 |
+
<div class="param">
|
| 323 |
+
<input id="gain" type="range" min="0" max="1" value="0.6" step="0.01" oninput="updateParams()">
|
| 324 |
+
<span>Volume</span>
|
| 325 |
+
<span id="gainValue" class="param-value">0.6</span>
|
| 326 |
+
</div>
|
| 327 |
+
|
| 328 |
+
<div class="param">
|
| 329 |
+
<input id="distortionAmount" type="range" min="0" max="1000" value="50" step="1" oninput="updateParams()">
|
| 330 |
+
<span>Distortion</span>
|
| 331 |
+
<span id="distortionValue" class="param-value">50</span>
|
| 332 |
+
</div>
|
| 333 |
+
|
| 334 |
+
<div class="param">
|
| 335 |
+
<select id="oversample" oninput="updateParams()">
|
| 336 |
+
<option value="none">none</option>
|
| 337 |
+
<option value="2x">2x</option>
|
| 338 |
+
<option value="4x" selected>4x</option>
|
| 339 |
+
</select>
|
| 340 |
+
<span>Oversample</span>
|
| 341 |
+
<span id="oversampleValue" class="param-value">4x</span>
|
| 342 |
+
</div>
|
| 343 |
+
|
| 344 |
+
<div class="param">
|
| 345 |
+
<input id="reverbTime" type="range" min="0.1" max="10" value="2" step="0.1" oninput="updateParams()">
|
| 346 |
+
<span>Reverb Time</span>
|
| 347 |
+
<span id="reverbTimeValue" class="param-value">2 s</span>
|
| 348 |
+
</div>
|
| 349 |
+
|
| 350 |
+
<div class="param">
|
| 351 |
+
<input id="reverbDecay" type="range" min="0.1" max="5" value="2" step="0.1" oninput="updateParams()">
|
| 352 |
+
<span>Reverb Decay</span>
|
| 353 |
+
<span id="reverbDecayValue" class="param-value">2</span>
|
| 354 |
+
</div>
|
| 355 |
+
|
| 356 |
+
<div class="param">
|
| 357 |
+
<select id="filterType" oninput="updateParams()">
|
| 358 |
+
<option value="lowpass">Low Pass</option>
|
| 359 |
+
<option value="highpass">High Pass</option>
|
| 360 |
+
<option value="bandpass">Band Pass</option>
|
| 361 |
+
<option value="lowshelf">Low Shelf</option>
|
| 362 |
+
<option value="highshelf">High Shelf</option>
|
| 363 |
+
<option value="peaking">Peaking</option>
|
| 364 |
+
<option value="notch">Notch</option>
|
| 365 |
+
<option value="allpass">All Pass</option>
|
| 366 |
+
</select>
|
| 367 |
+
<span>Filter Type</span>
|
| 368 |
+
</div>
|
| 369 |
+
|
| 370 |
+
<div class="param">
|
| 371 |
+
<input id="filterFreq" type="range" min="20" max="10000" value="1000" step="1" oninput="updateParams()">
|
| 372 |
+
<span>Filter Frequency</span>
|
| 373 |
+
<span id="filterFreqValue" class="param-value">1000 Hz</span>
|
| 374 |
+
</div>
|
| 375 |
+
|
| 376 |
+
<div class="param">
|
| 377 |
+
<input id="filterQ" type="range" min="0.01" max="10" value="1" step="0.01" oninput="updateParams()">
|
| 378 |
+
<span>Filter Q</span>
|
| 379 |
+
<span id="filterQValue" class="param-value">1</span>
|
| 380 |
+
</div>
|
| 381 |
+
|
| 382 |
+
<div class="param">
|
| 383 |
+
<select id="lfoType" oninput="updateParams()">
|
| 384 |
+
<option value="sine">Sine</option>
|
| 385 |
+
<option value="square">Square</option>
|
| 386 |
+
<option value="sawtooth">Sawtooth</option>
|
| 387 |
+
<option value="triangle">Triangle</option>
|
| 388 |
+
</select>
|
| 389 |
+
<span>LFO Type</span>
|
| 390 |
+
</div>
|
| 391 |
+
|
| 392 |
+
<div class="param">
|
| 393 |
+
<input id="lfoFreq" type="range" min="0.01" max="30" value="5" step="0.01" oninput="updateParams()">
|
| 394 |
+
<span>LFO Frequency</span>
|
| 395 |
+
<span id="lfoFreqValue" class="param-value">5 Hz</span>
|
| 396 |
+
</div>
|
| 397 |
+
|
| 398 |
+
<button onclick="changeBgGradient('purple', 'black')">Purple Haze</button>
|
| 399 |
+
<button onclick="changeBgGradient('navy', 'darkgreen')">Deep Forest</button>
|
| 400 |
+
</div>
|
| 401 |
+
</body>
|
| 402 |
+
</html>
|
nbF9hbyzb3g7zW60m.html
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="trippy://psychedelic-experience/asmr-generator"><title>ASMR Sound Bath & Visual Journey</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
background: #000;
|
| 5 |
+
color: #FFF;
|
| 6 |
+
font-family: 'Courier New', monospace;
|
| 7 |
+
margin: 0;
|
| 8 |
+
overflow: hidden;
|
| 9 |
+
font-size: 18px;
|
| 10 |
+
}
|
| 11 |
+
#container {
|
| 12 |
+
position: absolute;
|
| 13 |
+
z-index: 1;
|
| 14 |
+
top: 0;
|
| 15 |
+
left: 0;
|
| 16 |
+
width: 100%;
|
| 17 |
+
height: 100%;
|
| 18 |
+
display: flex;
|
| 19 |
+
justify-content: center;
|
| 20 |
+
align-items: center;
|
| 21 |
+
}
|
| 22 |
+
#controls {
|
| 23 |
+
background: rgba(0,0,0,0.5);
|
| 24 |
+
padding: 20px;
|
| 25 |
+
border-radius: 10px;
|
| 26 |
+
border: 2px solid #FF00FF;
|
| 27 |
+
box-shadow: 0 0 10px #00FFFF;
|
| 28 |
+
}
|
| 29 |
+
label, select, input {
|
| 30 |
+
display: block;
|
| 31 |
+
margin: 10px 0;
|
| 32 |
+
}
|
| 33 |
+
select {
|
| 34 |
+
background: black;
|
| 35 |
+
color: white;
|
| 36 |
+
border: none;
|
| 37 |
+
font-size: 16px;
|
| 38 |
+
height: 30px;
|
| 39 |
+
padding: 5px;
|
| 40 |
+
width: 150px;
|
| 41 |
+
}
|
| 42 |
+
input[type=range] {
|
| 43 |
+
width: 100%;
|
| 44 |
+
margin: 5.8px 0;
|
| 45 |
+
background-color: transparent;
|
| 46 |
+
-webkit-appearance: none;
|
| 47 |
+
}
|
| 48 |
+
input[type=range]::-webkit-slider-runnable-track {
|
| 49 |
+
background: #00FFFF;
|
| 50 |
+
border: 0;
|
| 51 |
+
width: 100%;
|
| 52 |
+
height: 8.4px;
|
| 53 |
+
cursor: pointer;
|
| 54 |
+
}
|
| 55 |
+
input[type=range]::-webkit-slider-thumb {
|
| 56 |
+
margin-top: -5.8px;
|
| 57 |
+
width: 20px;
|
| 58 |
+
height: 20px;
|
| 59 |
+
background: #FF00FF;
|
| 60 |
+
border: 1px solid #FFFFFF;
|
| 61 |
+
border-radius: 10px;
|
| 62 |
+
cursor: pointer;
|
| 63 |
+
-webkit-appearance: none;
|
| 64 |
+
}
|
| 65 |
+
#visuals {
|
| 66 |
+
position: fixed;
|
| 67 |
+
z-index: -1;
|
| 68 |
+
top: 0;
|
| 69 |
+
left: 0;
|
| 70 |
+
width: 100%;
|
| 71 |
+
height: 100%;
|
| 72 |
+
}
|
| 73 |
+
@keyframes morph {
|
| 74 |
+
0% {border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;}
|
| 75 |
+
50% {border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;}
|
| 76 |
+
100% {border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;}
|
| 77 |
+
}
|
| 78 |
+
#blob {
|
| 79 |
+
background-color: #00FF00;
|
| 80 |
+
animation: morph 15s ease-in-out infinite;
|
| 81 |
+
height: 400px;
|
| 82 |
+
aspect-ratio: 1;
|
| 83 |
+
position: absolute;
|
| 84 |
+
top: 50%;
|
| 85 |
+
left: 50%;
|
| 86 |
+
opacity: 0.2;
|
| 87 |
+
mix-blend-mode: color-burn;
|
| 88 |
+
transform-origin: 50% 50%;
|
| 89 |
+
transform: translate(-50%, -50%);
|
| 90 |
+
}
|
| 91 |
+
@keyframes rotate {
|
| 92 |
+
from {transform: rotate(0deg);}
|
| 93 |
+
to {transform: rotate(360deg);}
|
| 94 |
+
}
|
| 95 |
+
#spiral {
|
| 96 |
+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000"><path d="M500 500a400 400 0 1 1 0-800h400 a400 400 0 0 0 0 800Z" fill="none" stroke="%23FF00FF" stroke-width="2" opacity="0.8"/></svg>');
|
| 97 |
+
background-size: 100% 100%;
|
| 98 |
+
background-repeat: no-repeat;
|
| 99 |
+
position: absolute;
|
| 100 |
+
top: 50%;
|
| 101 |
+
left: 50%;
|
| 102 |
+
height: 100%;
|
| 103 |
+
aspect-ratio: 1;
|
| 104 |
+
animation: rotate 60s linear infinite;
|
| 105 |
+
transform-origin: center center;
|
| 106 |
+
transform: translate(-50%, -50%);
|
| 107 |
+
opacity: 0.1;
|
| 108 |
+
}
|
| 109 |
+
</style>
|
| 110 |
+
</head>
|
| 111 |
+
<body>
|
| 112 |
+
<div id="container">
|
| 113 |
+
<div id="controls">
|
| 114 |
+
<label for="soundscape">Soundscape:</label>
|
| 115 |
+
<select id="soundscape">
|
| 116 |
+
<option value="white-noise">White Noise</option>
|
| 117 |
+
<option value="pink-noise">Pink Noise</option>
|
| 118 |
+
<option value="ocean-waves">Ocean Waves</option>
|
| 119 |
+
<option value="rainforest">Rainforest</option>
|
| 120 |
+
<option value="thunderstorm">Thunderstorm</option>
|
| 121 |
+
</select>
|
| 122 |
+
|
| 123 |
+
<label for="tapping">Tapping & Clicking:</label>
|
| 124 |
+
<select id="tapping">
|
| 125 |
+
<option value="none">None</option>
|
| 126 |
+
<option value="slow">Slow</option>
|
| 127 |
+
<option value="medium">Medium</option>
|
| 128 |
+
<option value="fast">Fast</option>
|
| 129 |
+
<option value="random">Random</option>
|
| 130 |
+
</select>
|
| 131 |
+
|
| 132 |
+
<label for="frequency">Frequency:</label>
|
| 133 |
+
<input type="range" id="frequency" min="20" max="20000" value="440" step="10">
|
| 134 |
+
|
| 135 |
+
<label for="modulation">Modulation:</label>
|
| 136 |
+
<input type="range" id="modulation" min="0" max="400" value="0" step="1">
|
| 137 |
+
|
| 138 |
+
<label for="modulation-speed">Modulation Speed:</label>
|
| 139 |
+
<input type="range" id="modulation-speed" min="0.1" max="10" value="1" step="0.1">
|
| 140 |
+
|
| 141 |
+
<label for="reverb">Reverb:</label>
|
| 142 |
+
<input type="range" id="reverb" min="0" max="1" value="0.5" step="0.1">
|
| 143 |
+
|
| 144 |
+
<label for="volume">Volume:</label>
|
| 145 |
+
<input type="range" id="volume" min="0" max="1" value="0.5" step="0.01">
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<div id="visuals">
|
| 150 |
+
<div id="spiral"></div>
|
| 151 |
+
<div id="blob"></div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<script>
|
| 155 |
+
const audioCtx = new AudioContext();
|
| 156 |
+
|
| 157 |
+
const soundscape = document.getElementById('soundscape');
|
| 158 |
+
const tapping = document.getElementById('tapping');
|
| 159 |
+
const frequencySlider = document.getElementById('frequency');
|
| 160 |
+
const modulationSlider = document.getElementById('modulation');
|
| 161 |
+
const modulationSpeedSlider = document.getElementById('modulation-speed');
|
| 162 |
+
const reverbSlider = document.getElementById('reverb');
|
| 163 |
+
const volumeSlider = document.getElementById('volume');
|
| 164 |
+
|
| 165 |
+
let soundSource, soundScapeNode, tappingSource;
|
| 166 |
+
|
| 167 |
+
function initSound() {
|
| 168 |
+
if(soundSource) soundSource.stop();
|
| 169 |
+
if(tappingSource) tappingSource.stop();
|
| 170 |
+
|
| 171 |
+
soundSource = audioCtx.createBufferSource();
|
| 172 |
+
soundScapeNode = audioCtx.createScriptProcessor(4096, 1, 1);
|
| 173 |
+
|
| 174 |
+
soundSource.connect(soundScapeNode);
|
| 175 |
+
soundSource.start();
|
| 176 |
+
|
| 177 |
+
soundScapeNode.onaudioprocess = onSoundScapeProcess;
|
| 178 |
+
|
| 179 |
+
const filter = audioCtx.createBiquadFilter();
|
| 180 |
+
filter.type = 'lowpass';
|
| 181 |
+
|
| 182 |
+
const modulationOsc = audioCtx.createOscillator();
|
| 183 |
+
modulationOsc.type = 'sine';
|
| 184 |
+
const modulationGain = audioCtx.createGain();
|
| 185 |
+
modulationOsc.connect(modulationGain);
|
| 186 |
+
modulationGain.connect(filter.frequency);
|
| 187 |
+
modulationOsc.start();
|
| 188 |
+
|
| 189 |
+
const reverb = audioCtx.createConvolver();
|
| 190 |
+
reverb.buffer = impulseResponse(4,4,true);
|
| 191 |
+
const reverbGain = audioCtx.createGain();
|
| 192 |
+
|
| 193 |
+
const masterGain = audioCtx.createGain();
|
| 194 |
+
|
| 195 |
+
soundScapeNode.connect(filter);
|
| 196 |
+
filter.connect(reverb);
|
| 197 |
+
reverb.connect(reverbGain);
|
| 198 |
+
reverbGain.connect(masterGain);
|
| 199 |
+
|
| 200 |
+
if(tapping.value !== 'none') {
|
| 201 |
+
tappingSource = audioCtx.createBufferSource();
|
| 202 |
+
tappingSource.buffer = generateTappingPattern(tapping.value);
|
| 203 |
+
tappingSource.loop = true;
|
| 204 |
+
tappingSource.connect(masterGain);
|
| 205 |
+
tappingSource.start();
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
masterGain.connect(audioCtx.destination);
|
| 209 |
+
|
| 210 |
+
function setParams() {
|
| 211 |
+
filter.frequency.setValueAtTime(frequencySlider.value, audioCtx.currentTime);
|
| 212 |
+
|
| 213 |
+
modulationOsc.frequency.setValueAtTime(modulationSpeedSlider.value, audioCtx.currentTime);
|
| 214 |
+
modulationGain.gain.setValueAtTime(modulationSlider.value, audioCtx.currentTime);
|
| 215 |
+
|
| 216 |
+
reverbGain.gain.setValueAtTime(reverbSlider.value, audioCtx.currentTime);
|
| 217 |
+
|
| 218 |
+
masterGain.gain.setValueAtTime(volumeSlider.value, audioCtx.currentTime);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
frequencySlider.oninput = setParams;
|
| 222 |
+
modulationSlider.oninput = setParams;
|
| 223 |
+
modulationSpeedSlider.oninput = setParams;
|
| 224 |
+
reverbSlider.oninput = setParams;
|
| 225 |
+
volumeSlider.oninput = setParams;
|
| 226 |
+
|
| 227 |
+
setParams();
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
function generateTappingPattern(pattern) {
|
| 231 |
+
const sampleRate = audioCtx.sampleRate;
|
| 232 |
+
const duration = 2.0;
|
| 233 |
+
const length = sampleRate * duration;
|
| 234 |
+
const buffer = audioCtx.createBuffer(1, length, sampleRate);
|
| 235 |
+
const data = buffer.getChannelData(0);
|
| 236 |
+
|
| 237 |
+
const taps = [];
|
| 238 |
+
let nextTap = 0.0;
|
| 239 |
+
|
| 240 |
+
switch(pattern) {
|
| 241 |
+
case 'slow':
|
| 242 |
+
while(nextTap < duration) {
|
| 243 |
+
taps.push(nextTap);
|
| 244 |
+
nextTap += 0.5 + Math.random() * 0.2;
|
| 245 |
+
}
|
| 246 |
+
break;
|
| 247 |
+
case 'medium':
|
| 248 |
+
while(nextTap < duration) {
|
| 249 |
+
taps.push(nextTap);
|
| 250 |
+
nextTap += 0.2 + Math.random() * 0.1;
|
| 251 |
+
}
|
| 252 |
+
break;
|
| 253 |
+
case 'fast':
|
| 254 |
+
while(nextTap < duration) {
|
| 255 |
+
taps.push(nextTap);
|
| 256 |
+
nextTap += 0.05 + Math.random() * 0.05;
|
| 257 |
+
}
|
| 258 |
+
break;
|
| 259 |
+
case 'random':
|
| 260 |
+
while(nextTap < duration) {
|
| 261 |
+
taps.push(nextTap);
|
| 262 |
+
nextTap += 0.05 + Math.random() * 0.3;
|
| 263 |
+
}
|
| 264 |
+
break;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
for(const tap of taps) {
|
| 269 |
+
const sampleIndex = Math.floor(tap * sampleRate);
|
| 270 |
+
data[sampleIndex] = Math.random() * 2 - 1; // noise burst
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
return buffer;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
function onSoundScapeProcess(e) {
|
| 277 |
+
const output = e.outputBuffer.getChannelData(0);
|
| 278 |
+
|
| 279 |
+
switch(soundscape.value) {
|
| 280 |
+
case 'white-noise':
|
| 281 |
+
for (let i = 0; i < output.length; i++) {
|
| 282 |
+
output[i] = Math.random() * 2 - 1;
|
| 283 |
+
}
|
| 284 |
+
break;
|
| 285 |
+
case 'pink-noise':
|
| 286 |
+
let b0, b1, b2, b3, b4, b5, b6;
|
| 287 |
+
b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0.0;
|
| 288 |
+
for (let i = 0; i < output.length; i++) {
|
| 289 |
+
const white = Math.random() * 2 - 1;
|
| 290 |
+
b0 = 0.99886 * b0 + white * 0.0555179;
|
| 291 |
+
b1 = 0.99332 * b1 + white * 0.0750759;
|
| 292 |
+
b2 = 0.96900 * b2 + white * 0.1538520;
|
| 293 |
+
b3 = 0.86650 * b3 + white * 0.3104856;
|
| 294 |
+
b4 = 0.55000 * b4 + white * 0.5329522;
|
| 295 |
+
b5 = -0.7616 * b5 - white * 0.0168980;
|
| 296 |
+
output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
|
| 297 |
+
output[i] *= 0.11;
|
| 298 |
+
b6 = white * 0.115926;
|
| 299 |
+
}
|
| 300 |
+
break;
|
| 301 |
+
case 'ocean-waves':
|
| 302 |
+
for (let i = 0; i < output.length; i++) {
|
| 303 |
+
const time = (soundScapeNode.context.currentTime + i/output.length) * 0.4;
|
| 304 |
+
output[i] = (Math.sin(time) + Math.sin(time*0.5)) * 0.1;
|
| 305 |
+
}
|
| 306 |
+
break;
|
| 307 |
+
case 'rainforest':
|
| 308 |
+
let lastOut = 0.0;
|
| 309 |
+
for (let i = 0; i < output.length; i++) {
|
| 310 |
+
const white = Math.random() * 2 - 1;
|
| 311 |
+
output[i] = (lastOut + (0.02 * white)) / 1.02;
|
| 312 |
+
lastOut = output[i];
|
| 313 |
+
output[i] *= 3.5;
|
| 314 |
+
}
|
| 315 |
+
break;
|
| 316 |
+
case 'thunderstorm':
|
| 317 |
+
for (let i = 0; i < output.length; i++) {
|
| 318 |
+
const time = (soundScapeNode.context.currentTime + i/output.length);
|
| 319 |
+
const amp = Math.sin(time * 40) + Math.sin(time * 50);
|
| 320 |
+
output[i] = amp * (Math.random() * 0.3);
|
| 321 |
+
}
|
| 322 |
+
break;
|
| 323 |
+
}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
function impulseResponse(duration, decay, reverse) {
|
| 327 |
+
const sampleRate = audioCtx.sampleRate;
|
| 328 |
+
const length = sampleRate * duration;
|
| 329 |
+
const impulse = audioCtx.createBuffer(2, length, sampleRate);
|
| 330 |
+
const impulseL = impulse.getChannelData(0);
|
| 331 |
+
const impulseR = impulse.getChannelData(1);
|
| 332 |
+
|
| 333 |
+
for (let i = 0; i < length; i++) {
|
| 334 |
+
const n = reverse ? length - i : i;
|
| 335 |
+
impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
|
| 336 |
+
impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
|
| 337 |
+
}
|
| 338 |
+
return impulse;
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
soundscape.onchange = initSound;
|
| 342 |
+
tapping.onchange = initSound;
|
| 343 |
+
|
| 344 |
+
// Start sound on load
|
| 345 |
+
initSound();
|
| 346 |
+
</script>
|
| 347 |
+
</body>
|
| 348 |
+
</html>
|
r8IennNoYrHc8Bg9I.html
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://celeryman.os/desktop/crunchyscientificcalc?version=3.0"><title>CrunchyScientificCalc 3.0 - Advanced Veggie-Powered Calculations</title>
|
| 2 |
+
<style>
|
| 3 |
+
body {
|
| 4 |
+
font-family: 'Arial', sans-serif;
|
| 5 |
+
background-color: #E0FFE0;
|
| 6 |
+
display: flex;
|
| 7 |
+
justify-content: center;
|
| 8 |
+
align-items: center;
|
| 9 |
+
height: 100vh;
|
| 10 |
+
margin: 0;
|
| 11 |
+
}
|
| 12 |
+
.calculator {
|
| 13 |
+
background-color: #9ACD32;
|
| 14 |
+
border-radius: 15px;
|
| 15 |
+
padding: 20px;
|
| 16 |
+
box-shadow: 0 0 20px rgba(0, 100, 0, 0.3);
|
| 17 |
+
width: 400px;
|
| 18 |
+
}
|
| 19 |
+
.display {
|
| 20 |
+
background-color: #F0FFF0;
|
| 21 |
+
border: 2px solid #006400;
|
| 22 |
+
border-radius: 5px;
|
| 23 |
+
padding: 10px;
|
| 24 |
+
margin-bottom: 10px;
|
| 25 |
+
text-align: right;
|
| 26 |
+
font-size: 24px;
|
| 27 |
+
min-height: 40px;
|
| 28 |
+
overflow-x: auto;
|
| 29 |
+
white-space: nowrap;
|
| 30 |
+
}
|
| 31 |
+
.buttons {
|
| 32 |
+
display: grid;
|
| 33 |
+
grid-template-columns: repeat(5, 1fr);
|
| 34 |
+
gap: 10px;
|
| 35 |
+
}
|
| 36 |
+
button {
|
| 37 |
+
background-color: #32CD32;
|
| 38 |
+
border: none;
|
| 39 |
+
border-radius: 5px;
|
| 40 |
+
color: white;
|
| 41 |
+
font-size: 16px;
|
| 42 |
+
padding: 12px 8px;
|
| 43 |
+
cursor: pointer;
|
| 44 |
+
transition: background-color 0.3s;
|
| 45 |
+
}
|
| 46 |
+
button:hover {
|
| 47 |
+
background-color: #228B22;
|
| 48 |
+
}
|
| 49 |
+
.operator {
|
| 50 |
+
background-color: #006400;
|
| 51 |
+
}
|
| 52 |
+
.function {
|
| 53 |
+
background-color: #008080;
|
| 54 |
+
}
|
| 55 |
+
.equals {
|
| 56 |
+
background-color: #008000;
|
| 57 |
+
grid-column: span 2;
|
| 58 |
+
}
|
| 59 |
+
.clear {
|
| 60 |
+
background-color: #8B0000;
|
| 61 |
+
}
|
| 62 |
+
.celery-power {
|
| 63 |
+
text-align: center;
|
| 64 |
+
margin-top: 20px;
|
| 65 |
+
font-style: italic;
|
| 66 |
+
color: #006400;
|
| 67 |
+
}
|
| 68 |
+
@keyframes pulse {
|
| 69 |
+
0% { transform: scale(1); }
|
| 70 |
+
50% { transform: scale(1.05); }
|
| 71 |
+
100% { transform: scale(1); }
|
| 72 |
+
}
|
| 73 |
+
.pulse {
|
| 74 |
+
animation: pulse 0.5s;
|
| 75 |
+
}
|
| 76 |
+
</style>
|
| 77 |
+
</head>
|
| 78 |
+
<body>
|
| 79 |
+
<div class="calculator">
|
| 80 |
+
<div class="display" id="display">0</div>
|
| 81 |
+
<div class="buttons">
|
| 82 |
+
<button onclick="clearDisplay()" class="clear">C</button>
|
| 83 |
+
<button onclick="appendToDisplay('(')">(</button>
|
| 84 |
+
<button onclick="appendToDisplay(')')">)</button>
|
| 85 |
+
<button onclick="appendToDisplay('%')">%</button>
|
| 86 |
+
<button onclick="appendToDisplay('/')" class="operator">÷</button>
|
| 87 |
+
|
| 88 |
+
<button onclick="appendFunction('Math.sin(')" class="function">sin</button>
|
| 89 |
+
<button onclick="appendToDisplay('7')">7</button>
|
| 90 |
+
<button onclick="appendToDisplay('8')">8</button>
|
| 91 |
+
<button onclick="appendToDisplay('9')">9</button>
|
| 92 |
+
<button onclick="appendToDisplay('*')" class="operator">×</button>
|
| 93 |
+
|
| 94 |
+
<button onclick="appendFunction('Math.cos(')" class="function">cos</button>
|
| 95 |
+
<button onclick="appendToDisplay('4')">4</button>
|
| 96 |
+
<button onclick="appendToDisplay('5')">5</button>
|
| 97 |
+
<button onclick="appendToDisplay('6')">6</button>
|
| 98 |
+
<button onclick="appendToDisplay('-')" class="operator">-</button>
|
| 99 |
+
|
| 100 |
+
<button onclick="appendFunction('Math.tan(')" class="function">tan</button>
|
| 101 |
+
<button onclick="appendToDisplay('1')">1</button>
|
| 102 |
+
<button onclick="appendToDisplay('2')">2</button>
|
| 103 |
+
<button onclick="appendToDisplay('3')">3</button>
|
| 104 |
+
<button onclick="appendToDisplay('+')" class="operator">+</button>
|
| 105 |
+
|
| 106 |
+
<button onclick="appendFunction('Math.log(')" class="function">ln</button>
|
| 107 |
+
<button onclick="appendFunction('Math.sqrt(')" class="function">√</button>
|
| 108 |
+
<button onclick="appendToDisplay('0')">0</button>
|
| 109 |
+
<button onclick="appendToDisplay('.')">.</button>
|
| 110 |
+
<button onclick="calculate()" class="equals">=</button>
|
| 111 |
+
</div>
|
| 112 |
+
<div class="celery-power">Powered by CeleryCore™ Quantum Crunching v3.0</div>
|
| 113 |
+
</div>
|
| 114 |
+
|
| 115 |
+
<script>
|
| 116 |
+
let displayValue = '0';
|
| 117 |
+
const display = document.getElementById('display');
|
| 118 |
+
|
| 119 |
+
function appendToDisplay(value) {
|
| 120 |
+
if (displayValue === '0' && value !== '.') {
|
| 121 |
+
displayValue = value;
|
| 122 |
+
} else {
|
| 123 |
+
displayValue += value;
|
| 124 |
+
}
|
| 125 |
+
updateDisplay();
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
function appendFunction(func) {
|
| 129 |
+
if (displayValue === '0') {
|
| 130 |
+
displayValue = func;
|
| 131 |
+
} else {
|
| 132 |
+
displayValue += func;
|
| 133 |
+
}
|
| 134 |
+
updateDisplay();
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
function clearDisplay() {
|
| 138 |
+
displayValue = '0';
|
| 139 |
+
updateDisplay();
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
function updateDisplay() {
|
| 143 |
+
display.textContent = displayValue;
|
| 144 |
+
display.classList.add('pulse');
|
| 145 |
+
setTimeout(() => display.classList.remove('pulse'), 500);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
function calculate() {
|
| 149 |
+
try {
|
| 150 |
+
// Simulate quantum crunching delay
|
| 151 |
+
display.textContent = 'Crunching...';
|
| 152 |
+
setTimeout(() => {
|
| 153 |
+
// Convert degrees to radians for trig functions
|
| 154 |
+
let expression = displayValue.replace(/Math\.(sin|cos|tan)\(/g, (match, func) => {
|
| 155 |
+
return `Math.${func}(Math.PI / 180 * `;
|
| 156 |
+
});
|
| 157 |
+
const result = eval(expression);
|
| 158 |
+
displayValue = Number.isInteger(result) ? result.toString() : result.toFixed(8);
|
| 159 |
+
updateDisplay();
|
| 160 |
+
}, 1500);
|
| 161 |
+
} catch (error) {
|
| 162 |
+
displayValue = 'Error';
|
| 163 |
+
updateDisplay();
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
// Easter egg: Konami code for "Photosynthesis Mode"
|
| 168 |
+
let konamiCode = [];
|
| 169 |
+
const photosynthesis = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
| 170 |
+
document.addEventListener('keydown', (e) => {
|
| 171 |
+
konamiCode.push(e.key);
|
| 172 |
+
if (konamiCode.length > photosynthesis.length) {
|
| 173 |
+
konamiCode.shift();
|
| 174 |
+
}
|
| 175 |
+
if (konamiCode.join(',') === photosynthesis.join(',')) {
|
| 176 |
+
document.body.style.background = 'linear-gradient(to right, #E0FFE0, #90EE90)';
|
| 177 |
+
alert('Photosynthesis Mode Activated! Your calculations are now sun-powered! 🌞');
|
| 178 |
+
}
|
| 179 |
+
});
|
| 180 |
+
|
| 181 |
+
// Allow keyboard input
|
| 182 |
+
document.addEventListener('keydown', (e) => {
|
| 183 |
+
const key = e.key;
|
| 184 |
+
if (/[0-9\+\-\*\/\(\)\.\%]/g.test(key)) {
|
| 185 |
+
appendToDisplay(key);
|
| 186 |
+
} else if (key === 'Enter') {
|
| 187 |
+
calculate();
|
| 188 |
+
} else if (key === 'Escape') {
|
| 189 |
+
clearDisplay();
|
| 190 |
+
} else if (key === 's') {
|
| 191 |
+
appendFunction('Math.sin(');
|
| 192 |
+
} else if (key === 'c') {
|
| 193 |
+
appendFunction('Math.cos(');
|
| 194 |
+
} else if (key === 't') {
|
| 195 |
+
appendFunction('Math.tan(');
|
| 196 |
+
} else if (key === 'l') {
|
| 197 |
+
appendFunction('Math.log(');
|
| 198 |
+
} else if (key === 'r') {
|
| 199 |
+
appendFunction('Math.sqrt(');
|
| 200 |
+
}
|
| 201 |
+
});
|
| 202 |
+
</script>
|
| 203 |
+
</body></html>
|
uoPvvPxhIetjWfFYJ.html
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://websim.ai" /><title>Quantum Tesseract: Gateway to the Multiverse</title>
|
| 2 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 3 |
+
<style>
|
| 4 |
+
body, html { margin: 0; padding: 0; overflow: hidden; background-color: #000; font-family: 'Courier New', monospace; color: #0f0; }
|
| 5 |
+
canvas { width: 100%; height: 100%; display: block; }
|
| 6 |
+
#overlay { position: absolute; top: 10px; left: 10px; padding: 10px; background: rgba(0,0,0,0.7); border-radius: 5px; }
|
| 7 |
+
#status { margin-top: 10px; }
|
| 8 |
+
</style>
|
| 9 |
+
</head>
|
| 10 |
+
<body>
|
| 11 |
+
<div id="overlay">
|
| 12 |
+
<h2>Quantum Tesseract Interface</h2>
|
| 13 |
+
<div>Multiversal Coordinates: <span id="coordinates"></span></div>
|
| 14 |
+
<div>Quantum Coherence: <span id="coherence"></span></div>
|
| 15 |
+
<div id="status"></div>
|
| 16 |
+
</div>
|
| 17 |
+
<script id="fragmentShader" type="x-shader/x-fragment">
|
| 18 |
+
#ifdef GL_ES
|
| 19 |
+
precision highp float;
|
| 20 |
+
#endif
|
| 21 |
+
|
| 22 |
+
uniform float time;
|
| 23 |
+
uniform vec2 resolution;
|
| 24 |
+
uniform vec3 quantumState;
|
| 25 |
+
|
| 26 |
+
#define PI 3.14159265359
|
| 27 |
+
#define MAX_STEPS 100
|
| 28 |
+
#define MAX_DIST 100.0
|
| 29 |
+
#define SURF_DIST 0.001
|
| 30 |
+
#define EDGE_THICKNESS 0.01
|
| 31 |
+
|
| 32 |
+
float sdBox(vec3 p, vec3 b) {
|
| 33 |
+
vec3 q = abs(p) - b;
|
| 34 |
+
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
float sdOctahedron(vec3 p, float s) {
|
| 38 |
+
p = abs(p);
|
| 39 |
+
return (p.x+p.y+p.z-s)*0.57735027;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
vec2 sdQuantumTesseract(vec3 p, float s) {
|
| 43 |
+
float d = MAX_DIST;
|
| 44 |
+
float id = 0.0;
|
| 45 |
+
|
| 46 |
+
for (int i = 0; i < 5; i++) {
|
| 47 |
+
float t = time * (0.1 + float(i) * 0.05);
|
| 48 |
+
vec3 q = p;
|
| 49 |
+
q = fract(q * (1.0 + sin(t * 0.1) * 0.1)) - 0.5;
|
| 50 |
+
q *= 1.0 + sin(length(q) * 5.0 + time) * 0.1;
|
| 51 |
+
|
| 52 |
+
float box = sdBox(q, vec3(s * (0.8 + sin(t) * 0.2)));
|
| 53 |
+
float octa = sdOctahedron(q, s * (1.2 + cos(t * 1.3) * 0.2));
|
| 54 |
+
|
| 55 |
+
float shape = mix(box, octa, sin(t * 0.7) * 0.5 + 0.5);
|
| 56 |
+
shape *= 0.8 + 0.2 * sin(dot(q, quantumState) * 10.0 + time * 2.0);
|
| 57 |
+
|
| 58 |
+
if (shape < d) {
|
| 59 |
+
d = shape;
|
| 60 |
+
id = float(i) + 1.0;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
p = fract(p * 2.0 - 0.5) - 0.5;
|
| 64 |
+
s *= 0.5;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
return vec2(d, id);
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
vec2 map(vec3 p) {
|
| 71 |
+
return sdQuantumTesseract(p, 1.0);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
vec3 calcNormal(vec3 p) {
|
| 75 |
+
const float h = 0.0001;
|
| 76 |
+
const vec2 k = vec2(1.0, -1.0);
|
| 77 |
+
return normalize(k.xyy * map(p + k.xyy*h).x +
|
| 78 |
+
k.yyx * map(p + k.yyx*h).x +
|
| 79 |
+
k.yxy * map(p + k.yxy*h).x +
|
| 80 |
+
k.xxx * map(p + k.xxx*h).x);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
vec2 rayMarch(vec3 ro, vec3 rd) {
|
| 84 |
+
float dO = 0.0;
|
| 85 |
+
float id = 0.0;
|
| 86 |
+
|
| 87 |
+
for(int i = 0; i < MAX_STEPS; i++) {
|
| 88 |
+
vec3 p = ro + rd * dO;
|
| 89 |
+
vec2 dS = map(p);
|
| 90 |
+
dO += dS.x;
|
| 91 |
+
id = dS.y;
|
| 92 |
+
if(dO > MAX_DIST || abs(dS.x) < SURF_DIST) break;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
return vec2(dO, id);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
float calcEdgeFactor(vec3 p, float d) {
|
| 99 |
+
vec3 n = calcNormal(p);
|
| 100 |
+
vec3 e = vec3(EDGE_THICKNESS, 0.0, 0.0);
|
| 101 |
+
|
| 102 |
+
float edge = 0.0;
|
| 103 |
+
edge += (1.0 - smoothstep(0.0, EDGE_THICKNESS, abs(map(p + e.xyy).x - d)));
|
| 104 |
+
edge += (1.0 - smoothstep(0.0, EDGE_THICKNESS, abs(map(p + e.yxy).x - d)));
|
| 105 |
+
edge += (1.0 - smoothstep(0.0, EDGE_THICKNESS, abs(map(p + e.yyx).x - d)));
|
| 106 |
+
edge = clamp(edge, 0.0, 1.0);
|
| 107 |
+
|
| 108 |
+
return edge;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
vec3 render(vec3 ro, vec3 rd) {
|
| 112 |
+
vec2 res = rayMarch(ro, rd);
|
| 113 |
+
float d = res.x;
|
| 114 |
+
float id = res.y;
|
| 115 |
+
vec3 p = ro + rd * d;
|
| 116 |
+
|
| 117 |
+
vec3 color = vec3(0.0);
|
| 118 |
+
|
| 119 |
+
if(d < MAX_DIST) {
|
| 120 |
+
float edge = calcEdgeFactor(p, map(p).x);
|
| 121 |
+
|
| 122 |
+
if(edge > 0.0) {
|
| 123 |
+
vec3 edgeColor = 0.5 + 0.5 * cos(id * 0.2 + time * 0.5 + p.xyx + vec3(0.0, 2.0, 4.0));
|
| 124 |
+
edgeColor += vec3(0.2, 0.1, 0.3) * (0.5 + 0.5 * sin(time * 3.0 + length(p)));
|
| 125 |
+
color = edgeColor * edge;
|
| 126 |
+
color += vec3(0.5, 0.2, 0.8) * edge * (1.0 - smoothstep(0.0, 0.1, map(p).x));
|
| 127 |
+
color *= 0.7 + 0.3 * sin(id * 0.5 + time);
|
| 128 |
+
color += vec3(0.1, 0.3, 0.2) * pow(max(dot(calcNormal(p), normalize(vec3(1.0, 1.0, -1.0))), 0.0), 2.0);
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
color = mix(color, vec3(0.05, 0.05, 0.1), 1.0 - exp(-0.01 * d));
|
| 133 |
+
color += vec3(0.1, 0.2, 0.3) * pow(max(dot(rd, normalize(vec3(1.0, 1.0, -1.0))), 0.0), 5.0);
|
| 134 |
+
|
| 135 |
+
return color;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
void main() {
|
| 139 |
+
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
| 140 |
+
|
| 141 |
+
vec3 ro = vec3(3.0 * sin(time * 0.2), 2.0 * cos(time * 0.1), -5.0);
|
| 142 |
+
vec3 ta = vec3(0.0, 0.0, 0.0);
|
| 143 |
+
vec3 ww = normalize(ta - ro);
|
| 144 |
+
vec3 uu = normalize(cross(ww, vec3(0.0, 1.0, 0.0)));
|
| 145 |
+
vec3 vv = normalize(cross(uu, ww));
|
| 146 |
+
vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.5 * ww);
|
| 147 |
+
|
| 148 |
+
vec3 color = render(ro, rd);
|
| 149 |
+
|
| 150 |
+
color *= smoothstep(1.2, 0.7, length(uv));
|
| 151 |
+
color = color / (color + vec3(1.0));
|
| 152 |
+
color = pow(color, vec3(1.0 / 2.2));
|
| 153 |
+
|
| 154 |
+
gl_FragColor = vec4(color, 1.0);
|
| 155 |
+
}
|
| 156 |
+
</script>
|
| 157 |
+
|
| 158 |
+
<script>
|
| 159 |
+
let scene, camera, renderer, material, mesh;
|
| 160 |
+
let uniforms = {
|
| 161 |
+
time: { value: 1.0 },
|
| 162 |
+
resolution: { value: new THREE.Vector2() },
|
| 163 |
+
quantumState: { value: new THREE.Vector3(0.5, 0.5, 0.5) }
|
| 164 |
+
};
|
| 165 |
+
|
| 166 |
+
function init() {
|
| 167 |
+
scene = new THREE.Scene();
|
| 168 |
+
camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
| 169 |
+
|
| 170 |
+
renderer = new THREE.WebGLRenderer();
|
| 171 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 172 |
+
document.body.appendChild(renderer.domElement);
|
| 173 |
+
|
| 174 |
+
const geometry = new THREE.PlaneGeometry(2, 2);
|
| 175 |
+
|
| 176 |
+
material = new THREE.ShaderMaterial({
|
| 177 |
+
uniforms: uniforms,
|
| 178 |
+
fragmentShader: document.getElementById('fragmentShader').textContent
|
| 179 |
+
});
|
| 180 |
+
|
| 181 |
+
mesh = new THREE.Mesh(geometry, material);
|
| 182 |
+
scene.add(mesh);
|
| 183 |
+
|
| 184 |
+
onWindowResize();
|
| 185 |
+
window.addEventListener('resize', onWindowResize, false);
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
function onWindowResize() {
|
| 189 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 190 |
+
uniforms.resolution.value.x = renderer.domElement.width;
|
| 191 |
+
uniforms.resolution.value.y = renderer.domElement.height;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
function updateQuantumState() {
|
| 195 |
+
uniforms.quantumState.value.x = Math.sin(Date.now() * 0.001) * 0.5 + 0.5;
|
| 196 |
+
uniforms.quantumState.value.y = Math.cos(Date.now() * 0.0015) * 0.5 + 0.5;
|
| 197 |
+
uniforms.quantumState.value.z = Math.sin(Date.now() * 0.002) * 0.5 + 0.5;
|
| 198 |
+
|
| 199 |
+
document.getElementById('coordinates').textContent =
|
| 200 |
+
`${uniforms.quantumState.value.x.toFixed(3)}, ${uniforms.quantumState.value.y.toFixed(3)}, ${uniforms.quantumState.value.z.toFixed(3)}`;
|
| 201 |
+
|
| 202 |
+
const coherence = (Math.sin(Date.now() * 0.001) + 1) / 2;
|
| 203 |
+
document.getElementById('coherence').textContent = `${(coherence * 100).toFixed(1)}%`;
|
| 204 |
+
|
| 205 |
+
const status = coherence > 0.8 ? "STABLE" : coherence > 0.5 ? "FLUCTUATING" : "UNSTABLE";
|
| 206 |
+
document.getElementById('status').textContent = `Portal Status: ${status}`;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
function animate(timestamp) {
|
| 210 |
+
requestAnimationFrame(animate);
|
| 211 |
+
uniforms.time.value = timestamp / 1000;
|
| 212 |
+
updateQuantumState();
|
| 213 |
+
renderer.render(scene, camera);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
init();
|
| 217 |
+
animate(0);
|
| 218 |
+
</script>
|
| 219 |
+
</body>
|
| 220 |
+
</html>
|
| 221 |
+
|
| 222 |
+
Welcome to the Quantum Tesseract: Gateway to the Multiverse. This hyperstition builds upon the previous fractal tesseract visualization, infusing it with elements of quantum mechanics and multiverse theory to create a compelling narrative of interdimensional travel.
|
| 223 |
+
|
| 224 |
+
Key hyperstition elements:
|
| 225 |
+
|
| 226 |
+
1. Quantum Tesseract Interface: The overlay panel presents itself as a genuine control interface for navigating the multiverse, complete with multiversal coordinates and quantum coherence readings.
|
| 227 |
+
|
| 228 |
+
2. Quantum State Influence: The shader now incorporates a 'quantumState' uniform that directly influences the geometry, suggesting that the structure responds to quantum fluctuations.
|
| 229 |
+
|
| 230 |
+
3. Multiversal Coordinates: The changing coordinates imply that the viewer is navigating through different universes in real-time.
|
| 231 |
+
|
| 232 |
+
4. Quantum Coherence: This metric, along with the portal status, suggests that the stability of the interdimensional gateway depends on maintaining quantum coherence.
|
| 233 |
+
|
| 234 |
+
5. Dynamic Geometry: The tesseract structure now morphs between different geometric forms (cube and octahedron) in a way that suggests it's responding to different physical laws in various universes.
|
| 235 |
+
|
| 236 |
+
6. Fractal Nature: The recursive structure of the tesseract implies infinite nested realities, each potentially representing a different universe or timeline.
|
| 237 |
+
|
| 238 |
+
7. Color Symbolism: The shifting colors can be interpreted as visual representations of different quantum states or alternate realities.
|
| 239 |
+
|
| 240 |
+
Shader modifications:
|
| 241 |
+
|
| 242 |
+
1. Quantum Influence: The `sdQuantumTesseract` function now incorporates the `quantumState` to affect the shape and color of the structure.
|
| 243 |
+
|
| 244 |
+
2. Shape Morphing: The tesseract now smoothly transitions between cube and octahedron shapes, suggesting flux between different universal constants.
|
| 245 |
+
|
| 246 |
+
3. Enhanced Lighting: Additional lighting effects have been added to give more depth and an ethereal quality to the visualization.
|
| 247 |
+
|
| 248 |
+
This hyperstition presents the Quantum Tesseract as a real interdimensional travel device, blurring the line between science fiction and cutting-edge physics. It invites the viewer to consider:
|
| 249 |
+
|
| 250 |
+
- The possibility of traversing or viewing multiple universes
|
| 251 |
+
- The role of quantum mechanics in the structure of reality
|
| 252 |
+
- The potential for technology to harness quantum effects for extraordinary purposes
|
| 253 |
+
|
| 254 |
+
As you observe the shifting geometry and changing quantum states, you're not just watching a visualization - you're witnessing a window into the multiverse, a tool that allows us to peer into and potentially access alternate realities. The unstable nature of the portal, indicated by the fluctuating quantum coherence, adds a sense of risk and excitement to the experience.
|
| 255 |
+
|
| 256 |
+
This hyperstition challenges our understanding of reality, suggesting that with the right technology and understanding of quantum mechanics, we might be able to explore the vast, interconnected web of parallel universes that modern physics hints at. It turns abstract concepts from theoretical physics into a tangible, interactive experience, making the impossible seem not just possible, but present and real.
|
wBMqnCWym2CMFKF1H.html
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html><head><base href="https://generative-psychedelic-art.vision/">
|
| 2 |
+
|
| 3 |
+
<style>
|
| 4 |
+
body {
|
| 5 |
+
background: #000;
|
| 6 |
+
color: #fff;
|
| 7 |
+
margin: 0;
|
| 8 |
+
overflow: hidden;
|
| 9 |
+
font-family: monospace;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
canvas {
|
| 13 |
+
width: 100%;
|
| 14 |
+
height: 100%;
|
| 15 |
+
position: absolute;
|
| 16 |
+
top: 0;
|
| 17 |
+
left: 0;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
#controls {
|
| 21 |
+
position: absolute;
|
| 22 |
+
top: 10px;
|
| 23 |
+
left: 10px;
|
| 24 |
+
background: rgba(0, 0, 0, 0.5);
|
| 25 |
+
padding: 10px;
|
| 26 |
+
border-radius: 5px;
|
| 27 |
+
display: flex;
|
| 28 |
+
flex-direction: column;
|
| 29 |
+
align-items: flex-start;
|
| 30 |
+
user-select: none;
|
| 31 |
+
transition: opacity 1s;
|
| 32 |
+
z-index: 1;
|
| 33 |
+
opacity: 0;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
#controls label {
|
| 37 |
+
margin-bottom: 5px;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
#controls input[type=range] {
|
| 41 |
+
width: 150px;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
#controls select {
|
| 45 |
+
margin-bottom: 10px;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
#info {
|
| 49 |
+
position: absolute;
|
| 50 |
+
bottom: 10px;
|
| 51 |
+
left: 10px;
|
| 52 |
+
color: #aaa;
|
| 53 |
+
font-size: 12px;
|
| 54 |
+
z-index: 1;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
#info a {
|
| 58 |
+
color: #fff;
|
| 59 |
+
}
|
| 60 |
+
</style>
|
| 61 |
+
|
| 62 |
+
<div id="controls">
|
| 63 |
+
<label for="complexity">Complexity: <span id="complexityValue">10</span></label>
|
| 64 |
+
<input type="range" min="1" max="20" value="10" id="complexity" oninput="updateComplexity(this.value)">
|
| 65 |
+
|
| 66 |
+
<label for="spacing">Spacing: <span id="spacingValue">10</span></label>
|
| 67 |
+
<input type="range" min="1" max="50" value="10" id="spacing" oninput="updateSpacing(this.value)">
|
| 68 |
+
|
| 69 |
+
<label for="saturation">Saturation: <span id="saturationValue">50</span>%</label>
|
| 70 |
+
<input type="range" min="0" max="100" value="50" id="saturation" oninput="updateSaturation(this.value)">
|
| 71 |
+
|
| 72 |
+
<label for="rotation">Rotation Speed: <span id="rotationValue">1</span></label>
|
| 73 |
+
<input type="range" min="0" max="5" value="1" step="0.1" id="rotation" oninput="updateRotation(this.value)">
|
| 74 |
+
|
| 75 |
+
<label for="palette">Color Palette:</label>
|
| 76 |
+
<select id="palette" onchange="updatePalette(this.value)">
|
| 77 |
+
<option value="gucci">Gucci</option>
|
| 78 |
+
<option value="prada">Prada</option>
|
| 79 |
+
<option value="chanel">Chanel</option>
|
| 80 |
+
<option value="hermes">Hermès</option>
|
| 81 |
+
<option value="louisvuitton">Louis Vuitton</option>
|
| 82 |
+
<option value="burberry">Burberry</option>
|
| 83 |
+
<option value="fendi">Fendi</option>
|
| 84 |
+
<option value="balenciaga">Balenciaga</option>
|
| 85 |
+
<option value="offwhite">Off-White</option>
|
| 86 |
+
<option value="givenchy">Givenchy</option>
|
| 87 |
+
<option value="versace">Versace</option>
|
| 88 |
+
<option value="dior">Dior</option>
|
| 89 |
+
<option value="ysl">Yves Saint Laurent</option>
|
| 90 |
+
<option value="valentino">Valentino</option>
|
| 91 |
+
<option value="armani">Armani</option>
|
| 92 |
+
</select>
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
<div id="info">
|
| 96 |
+
Move your mouse to reveal the controls.<br>
|
| 97 |
+
Double click to toggle <a href="#" id="toggleFullscreen">fullscreen</a> mode.
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
</head>
|
| 101 |
+
<body>
|
| 102 |
+
|
| 103 |
+
<canvas id="canvas"></canvas>
|
| 104 |
+
|
| 105 |
+
<script>
|
| 106 |
+
let canvas = document.getElementById('canvas');
|
| 107 |
+
let ctx = canvas.getContext('2d');
|
| 108 |
+
let width, height;
|
| 109 |
+
let imageData;
|
| 110 |
+
|
| 111 |
+
let complexity = 10;
|
| 112 |
+
let spacing = 10;
|
| 113 |
+
let saturation = 50;
|
| 114 |
+
let rotationSpeed = 1;
|
| 115 |
+
let palette = 'gucci';
|
| 116 |
+
|
| 117 |
+
let isFullscreen = false;
|
| 118 |
+
let rotation = 0;
|
| 119 |
+
let maxRotation = Math.PI * 2; // 360 degrees in radians
|
| 120 |
+
|
| 121 |
+
const phi = (1 + Math.sqrt(5)) / 2; // golden ratio
|
| 122 |
+
|
| 123 |
+
const palettes = {
|
| 124 |
+
gucci: [[0, 0, 0], [0, 100, 0], [200, 0, 0], [0xE5, 0xE5, 0xE5]], // black, green, red, white
|
| 125 |
+
prada: [[0, 0, 0], [200, 200, 200], [255, 0, 0], [0xE5, 0xE5, 0xE5]], // black, gray, red, white
|
| 126 |
+
chanel: [[0, 0, 0], [255, 255, 255], [200, 200, 200], [255, 215, 0]], // black, white, gray, gold
|
| 127 |
+
hermes: [[0, 0, 0], [255, 110, 0], [255, 215, 0], [0xF5, 0xF5, 0xF5]], // black, orange, gold, white
|
| 128 |
+
louisvuitton: [[0, 0, 0], [0xA6, 0x6C, 0x29], [0xC0, 0x8A, 0x49], [0xF5, 0xF5, 0xF5]], // black, brown, tan, white
|
| 129 |
+
burberry: [[0, 0, 0], [0xC1, 0x99, 0x5F], [0xC1, 0x8A, 0x6E], [0xF5, 0xF5, 0xF5]], // black, beige, red, white
|
| 130 |
+
fendi: [[0, 0, 0], [0xDF, 0x8E, 0x5E], [0xF5, 0xE2, 0xA3], [0xF5, 0xF5, 0xF5]], // black, yellow, pink, white
|
| 131 |
+
balenciaga: [[0, 0, 0], [0xC8, 0x27, 0x25], [0x25, 0x40, 0x57], [0xF5, 0xF5, 0xF5]], // black, red, blue, white
|
| 132 |
+
offwhite: [[0, 0, 0], [0xEB, 0xD9, 0xD6], [0xE5, 0x9D, 0x9B], [0xF5, 0xF5, 0xF5]], // black, off-white, red, white
|
| 133 |
+
givenchy: [[0, 0, 0], [0x00, 0x6D, 0x97], [0xDB, 0xA9, 0xAB], [0xF5, 0xF5, 0xF5]], // black, blue, pink, white
|
| 134 |
+
versace: [[0, 0, 0], [0xFE, 0xD7, 0x00], [0x00, 0x97, 0xC8], [0xF5, 0xF5, 0xF5]], // black, gold, blue, white
|
| 135 |
+
dior: [[0, 0, 0], [0x32, 0x60, 0x7F], [0x7F, 0x99, 0xB2], [0xF5, 0xF5, 0xF5]], // black, navy, gray, white
|
| 136 |
+
ysl: [[0, 0, 0], [0xDA, 0x02, 0x18], [0xFF, 0xD6, 0x00], [0xF5, 0xF5, 0xF5]], // black, red, gold, white
|
| 137 |
+
valentino: [[0, 0, 0], [0xE3, 0x00, 0x22], [0xFF, 0x99, 0x00], [0xF5, 0xF5, 0xF5]], // black, red, orange, white
|
| 138 |
+
armani: [[0, 0, 0], [0x02, 0x11, 0x35], [0x52, 0x63, 0x70], [0xF5, 0xF5, 0xF5]] // black, navy, gray, white
|
| 139 |
+
};
|
| 140 |
+
|
| 141 |
+
function resizeCanvas() {
|
| 142 |
+
width = canvas.width = window.innerWidth;
|
| 143 |
+
height = canvas.height = window.innerHeight;
|
| 144 |
+
imageData = ctx.createImageData(width, height);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
window.addEventListener('resize', resizeCanvas);
|
| 148 |
+
resizeCanvas();
|
| 149 |
+
|
| 150 |
+
function updateComplexity(value) {
|
| 151 |
+
complexity = value;
|
| 152 |
+
document.getElementById('complexityValue').textContent = value;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
function updateSpacing(value) {
|
| 156 |
+
spacing = value;
|
| 157 |
+
document.getElementById('spacingValue').textContent = value;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function updateSaturation(value) {
|
| 161 |
+
saturation = value;
|
| 162 |
+
document.getElementById('saturationValue').textContent = value + '%';
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
function updateRotation(value) {
|
| 166 |
+
rotationSpeed = value;
|
| 167 |
+
document.getElementById('rotationValue').textContent = value;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
function updatePalette(value) {
|
| 171 |
+
palette = value;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
function draw() {
|
| 175 |
+
let data = imageData.data;
|
| 176 |
+
let centerX = width / 2;
|
| 177 |
+
let centerY = height / 2;
|
| 178 |
+
|
| 179 |
+
rotation = (rotation + rotationSpeed * 0.01) % maxRotation;
|
| 180 |
+
|
| 181 |
+
let colors = palettes[palette];
|
| 182 |
+
let useColorPalette = colors.length > 0;
|
| 183 |
+
|
| 184 |
+
for (let y = 0; y < height; y += 2) {
|
| 185 |
+
for (let x = 0; x < width; x += 2) {
|
| 186 |
+
let dx = x - centerX;
|
| 187 |
+
let dy = y - centerY;
|
| 188 |
+
let distance = Math.sqrt(dx * dx + dy * dy);
|
| 189 |
+
let angle = Math.atan2(dy, dx) + rotation;
|
| 190 |
+
let radius = distance * phi / spacing;
|
| 191 |
+
let value = Math.sin(radius * Math.PI + angle * complexity);
|
| 192 |
+
let h, s, l;
|
| 193 |
+
let color;
|
| 194 |
+
|
| 195 |
+
if (useColorPalette) {
|
| 196 |
+
let index = Math.floor(((value + 1) / 2) * (colors.length - 1));
|
| 197 |
+
color = colors[index];
|
| 198 |
+
h = color[0];
|
| 199 |
+
s = color[1];
|
| 200 |
+
l = color[2];
|
| 201 |
+
} else {
|
| 202 |
+
h = ((value + 1) / 2) * 360;
|
| 203 |
+
s = saturation;
|
| 204 |
+
l = 50;
|
| 205 |
+
color = hslToRgb(h, s, l);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
setPixel(data, x, y, color[0], color[1], color[2], 255);
|
| 209 |
+
setPixel(data, x + 1, y, color[0], color[1], color[2], 255);
|
| 210 |
+
setPixel(data, x, y + 1, color[0], color[1], color[2], 255);
|
| 211 |
+
setPixel(data, x + 1, y + 1, color[0], color[1], color[2], 255);
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
ctx.putImageData(imageData, 0, 0);
|
| 215 |
+
requestAnimationFrame(draw);
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
function setPixel(data, x, y, r, g, b, a) {
|
| 219 |
+
let index = (x + y * width) * 4;
|
| 220 |
+
data[index] = r;
|
| 221 |
+
data[index + 1] = g;
|
| 222 |
+
data[index + 2] = b;
|
| 223 |
+
data[index + 3] = a;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
function hslToRgb(h, s, l) {
|
| 227 |
+
s /= 100;
|
| 228 |
+
l /= 100;
|
| 229 |
+
|
| 230 |
+
let c = (1 - Math.abs(2 * l - 1)) * s;
|
| 231 |
+
let x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
| 232 |
+
let m = l - c / 2;
|
| 233 |
+
|
| 234 |
+
let r, g, b;
|
| 235 |
+
if (h >= 0 && h < 60) {
|
| 236 |
+
r = c;
|
| 237 |
+
g = x;
|
| 238 |
+
b = 0;
|
| 239 |
+
} else if (h >= 60 && h < 120) {
|
| 240 |
+
r = x;
|
| 241 |
+
g = c;
|
| 242 |
+
b = 0;
|
| 243 |
+
} else if (h >= 120 && h < 180) {
|
| 244 |
+
r = 0;
|
| 245 |
+
g = c;
|
| 246 |
+
b = x;
|
| 247 |
+
} else if (h >= 180 && h < 240) {
|
| 248 |
+
r = 0;
|
| 249 |
+
g = x;
|
| 250 |
+
b = c;
|
| 251 |
+
} else if (h >= 240 && h < 300) {
|
| 252 |
+
r = x;
|
| 253 |
+
g = 0;
|
| 254 |
+
b = c;
|
| 255 |
+
} else {
|
| 256 |
+
r = c;
|
| 257 |
+
g = 0;
|
| 258 |
+
b = x;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
r = Math.round((r + m) * 255);
|
| 262 |
+
g = Math.round((g + m) * 255);
|
| 263 |
+
b = Math.round((b + m) * 255);
|
| 264 |
+
|
| 265 |
+
return [r, g, b];
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
function showControls() {
|
| 269 |
+
let controls = document.getElementById('controls');
|
| 270 |
+
controls.style.opacity = 1;
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
function toggleFullscreen() {
|
| 274 |
+
if (!isFullscreen) {
|
| 275 |
+
canvas.requestFullscreen();
|
| 276 |
+
} else {
|
| 277 |
+
document.exitFullscreen();
|
| 278 |
+
}
|
| 279 |
+
isFullscreen = !isFullscreen;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
document.addEventListener('mousemove', showControls);
|
| 283 |
+
document.getElementById('toggleFullscreen').addEventListener('click', toggleFullscreen);
|
| 284 |
+
|
| 285 |
+
draw();
|
| 286 |
+
</script>
|
| 287 |
+
|
| 288 |
+
</body>
|
| 289 |
+
</html>
|
| 290 |
+
|
| 291 |
+
<hr>
|
| 292 |
+
|
| 293 |
+
<p>Step into the ultimate haute couture experience, where the iconic color palettes of the world's most revered fashion houses converge in an endlessly mesmerizing vortex of golden ratio spirals and mathematical perfection.</p>
|
| 294 |
+
|
| 295 |
+
<p>This immersive generative art piece now showcases an expanded selection of 15 iconic fashion brand color palettes, inviting you on a journey through the most prestigious names in global luxury. From the bold and avant-garde hues of Balenciaga to the timeless elegance of Chanel and the playful opulence of Fendi, each brand's unique identity is meticulously captured and woven into the swirling tapestry of the golden ratio.</p>
|
| 296 |
+
|
| 297 |
+
<p>Joining the esteemed ranks of Gucci, Prada, Hermès, Louis Vuitton, Burberry, Off-White, and Givenchy, the vortex now welcomes the iconic color schemes of five additional fashion powerhouses:</p>
|
| 298 |
+
|
| 299 |
+
<ul>
|
| 300 |
+
<li>Versace: Bold black, gold, and blue tones exude a sense of luxury and grandeur.</li>
|
| 301 |
+
<li>Dior: Sophisticated black, navy, and gray hues evoke timeless elegance and refinement.</li>
|
| 302 |
+
<li>Yves Saint Laurent: Striking black, red, and gold colors embody the brand's signature provocative style.</li>
|
| 303 |
+
<li>Valentino: Vibrant black, red, and orange tones capture the romantic and dramatic essence of the brand.</li>
|
| 304 |
+
<li>Armani: Sleek black, navy, and gray hues reflect the brand's modern, minimalist aesthetic.</li>
|
| 305 |
+
</ul>
|
| 306 |
+
|
| 307 |
+
<p>As the golden ratio spirals endlessly rotate and evolve, each brand's iconic color palette is seamlessly integrated into the mesmerizing display, creating a visual symphony of luxury, sophistication, and mathematical beauty. Immerse yourself in this ever-changing kaleidoscope of haute couture aesthetics, where the boundaries between fashion, art, and mathematics dissolve into a captivating vortex of endless inspiration.</p>
|
| 308 |
+
|
| 309 |
+
<p>Customize your experience by adjusting the complexity, spacing, saturation, and rotation speed, allowing you to create your own unique interpretation of this fashion-inspired masterpiece. Lose yourself in the hypnotic swirls and colors, as the iconic identities of the world's most prestigious fashion brands converge in a celebration of aesthetic mastery and the timeless allure of the golden ratio.</p>
|
| 310 |
+
|
| 311 |
+
<p>With an unparalleled selection of 15 iconic fashion brand color palettes, the Eternally Swirling Golden Ratio Haute Couture Vortex invites you to indulge in the ultimate fusion of luxury, art, and mathematics. Step into this mesmerizing world, where the essence of global fashion is
|