├── LICENSE ├── README.md ├── core └── draw.js ├── index.html ├── methods ├── perlin-noise.js ├── value-noise.js └── white-noise.js ├── screen ├── canvas.js ├── globals.js └── ui.js ├── styles.css ├── utils ├── draw.js ├── math.js ├── misc.js └── worker.js └── visualisations ├── 1d-ball.js ├── 1d-color-gradient.js ├── 1d-colorful-ball.js ├── 1d-colorful-line.js ├── 1d-colorful-triangle.js ├── 1d-line.js ├── 1d-radius-ball.js ├── 1d-rgb-lines.js ├── 2d-colorful-image.js ├── 2d-colorful-marble-image.js ├── 2d-colorful-wood-image.js ├── 2d-grayscale-image.js ├── 2d-marble-image.js ├── 2d-terrain.js └── 2d-wood-image.js /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019, Rameş Aliyev 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visual Noises 2 | 3 | [Go play with it ->](https://ramesaliyev.com/visual-noises/) 4 | 5 | 1D and 2D visualisations of White Noise, Value Noise and Perlin Noise. For further understanding refer to [learning resources](https://github.com/ramesaliyev/visual-noises/issues/1). 6 | 7 | ![Screenshot](https://user-images.githubusercontent.com/2088512/52519903-ba4d4c80-2c73-11e9-9f22-bcb4cb39aa2b.png) -------------------------------------------------------------------------------- /core/draw.js: -------------------------------------------------------------------------------- 1 | let drawScheduled = false; 2 | 3 | function scheduleDraw() { 4 | drawScheduled = true; 5 | 6 | window.requestAnimationFrame(() => { 7 | drawScheduled = false; 8 | draw(true); 9 | }); 10 | } 11 | 12 | function draw(schedule) { 13 | if (drawScheduled) { 14 | return false; 15 | } 16 | 17 | if (leaveTail) { 18 | context.globalAlpha = 0.1; 19 | context.fillStyle = '#000'; 20 | context.fillRect(0, 0, screenWidth, screenHeight); 21 | context.globalAlpha = 1; 22 | } else if (clearCanvas) { 23 | context.clearRect(0, 0, screenWidth, screenHeight); 24 | } 25 | 26 | const { 27 | visualisationFn, 28 | methodFn, 29 | filterFn, 30 | outputFilterFn, 31 | } = getCurrentState(); 32 | 33 | const isAsync = visualisationFn({ 34 | getValueFn: ((x = 0, y = 0) => { 35 | let noiseSum = 0; 36 | let _amplitude = 0.5; 37 | let _frequency = frequency; 38 | 39 | let xOffset = yOffset = offset; 40 | 41 | if (!syncOffsetsXY) { 42 | yOffset = 0; 43 | } 44 | 45 | let maxValue = 0; 46 | 47 | for (let i = 0; i < octave; i++) { 48 | const noise = methodFn( 49 | (x + xOffset) * _frequency, 50 | (y + yOffset) * _frequency, 51 | filterFn 52 | ) 53 | 54 | noiseSum += outputFilterFn(noise) * _amplitude; 55 | 56 | maxValue += _amplitude; 57 | _frequency *= lacunarity; 58 | _amplitude *= gain; 59 | } 60 | 61 | return (noiseSum / maxValue) * amplitude; 62 | }), 63 | offsetX: 200, 64 | offsetY: 0, 65 | width: screenWidth - 200, 66 | height: screenHeight - 100, 67 | done: () => !stopped && scheduleDraw() 68 | }); 69 | 70 | if (!paused) { 71 | offsetInput.value = parseFloat(offset.toFixed('10')); 72 | offset += speed; 73 | } 74 | 75 | if (stopped && debugMode) console.log('Draw stopped.'); 76 | 77 | clearCanvas = !isAsync; 78 | 79 | if (!stopped && schedule && !isAsync) { 80 | scheduleDraw(); 81 | } 82 | } 83 | 84 | // Initialize seed. 85 | setRandomSeed(seed); 86 | 87 | // Warm up noise algorithms. 88 | ValueNoise2D({}); 89 | PerlinNoise2D({}); 90 | 91 | onResize(); 92 | 93 | applySettings(visualisationDefaults['1d-line']); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Visual Noises @ ramesaliyev 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |

Visual
Noises

16 |
17 |
18 |
19 |
20 |
Preset
21 |
22 | 28 |
29 |
30 |
31 |
Visualisation
32 |
33 | 50 |
51 |
52 |
53 |
Noise Algorithm
54 |
55 | 60 |
61 |
62 |
63 |
Filter
64 |
65 | 71 |
72 |
73 |
74 |
Output Filter
75 |
76 | 80 |
81 |
82 |
83 |
Offset
84 |
85 | 86 |
87 |
88 |
89 |
Offset Increment Speed
90 |
91 | 92 |
93 |
94 |
95 |
Frequency
96 |
97 | 98 |
99 |
100 |
101 |
Amplitude
102 |
103 | 104 |
105 |
106 |
107 |
Octave
108 |
109 | 110 |
111 |
112 |
113 |
Octave Lacunarity
114 |
115 | 116 |
117 |
118 |
119 |
Octave Gain
120 |
121 | 122 |
123 |
124 |
125 |
Actions
126 |
127 | 128 | 129 | 130 | 131 |
132 |
133 |
134 |
Seed
135 |
136 | 137 |
138 |
139 |
140 | 144 |
145 |
146 | 150 |
151 |
152 | 156 |
157 |
158 |
159 | 163 |
164 |
165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | onResize(true)); 37 | 38 | body.appendChild(canvas); 39 | -------------------------------------------------------------------------------- /screen/globals.js: -------------------------------------------------------------------------------- 1 | let debugMode = false; 2 | 3 | let screenWidth; 4 | let screenHeight; 5 | let mouseX; 6 | let mouseY; 7 | 8 | let visualisation = '1d-line'; 9 | let method = 'value-noise-2d'; 10 | let filter = 'smoothstep'; 11 | let outputFilter = 'none'; 12 | 13 | let seed = Date.now(); 14 | 15 | let stopped = false; 16 | let paused = false; 17 | let leaveTail = false; 18 | let applyDefaults = true; 19 | let syncOffsetsXY = false; 20 | let clearCanvas = true; 21 | 22 | let speed; 23 | let offset = 0; 24 | let amplitude; 25 | let frequency; 26 | 27 | let octave = 1; 28 | let lacunarity = 1; 29 | let gain = 1; -------------------------------------------------------------------------------- /screen/ui.js: -------------------------------------------------------------------------------- 1 | const presetSelect = document.getElementById('presetSelect'); 2 | const visualisationSelect = document.getElementById('visualisationSelect'); 3 | const methodSelect = document.getElementById('methodSelect'); 4 | const filterSelect = document.getElementById('filterSelect'); 5 | const outputFilterSelect = document.getElementById('outputFilterSelect'); 6 | const speedInput = document.getElementById('speedInput'); 7 | const offsetInput = document.getElementById('offsetInput'); 8 | const frequencyInput = document.getElementById('frequencyInput'); 9 | const amplitudeInput = document.getElementById('amplitudeInput'); 10 | const octaveInput = document.getElementById('octaveInput'); 11 | const lacunarityInput = document.getElementById('lacunarityInput'); 12 | const gainInput = document.getElementById('gainInput'); 13 | const drawButton = document.getElementById('drawButton'); 14 | const playPauseButton = document.getElementById('playPauseButton'); 15 | const incrementOffsetButton = document.getElementById('incrementOffsetButton'); 16 | const resetOffsetButton = document.getElementById('resetOffsetButton'); 17 | const seedInput = document.getElementById('seedInput'); 18 | 19 | const leaveTailInput = document.getElementById('leaveTailInput'); 20 | const applyDefaultsInput = document.getElementById('applyDefaultsInput'); 21 | const syncOffsetsInput = document.getElementById('syncOffsetsInput'); 22 | 23 | const elements = [ 24 | presetSelect, 25 | visualisationSelect, 26 | methodSelect, 27 | filterSelect, 28 | outputFilterSelect, 29 | speedInput, 30 | offsetInput, 31 | frequencyInput, 32 | amplitudeInput, 33 | octaveInput, 34 | lacunarityInput, 35 | gainInput, 36 | drawButton, 37 | playPauseButton, 38 | incrementOffsetButton, 39 | resetOffsetButton, 40 | seedInput, 41 | leaveTailInput, 42 | applyDefaultsInput, 43 | syncOffsetsInput 44 | ]; 45 | 46 | const visualisationFnsMap = { 47 | '1d-line': draw1DLine, 48 | '1d-colorful-line': draw1DColorfulLine, 49 | '1d-color-gradient': draw1DColorGradient, 50 | '1d-rgb-lines': draw1DRGBLines, 51 | '1d-ball': draw1DBall, 52 | '1d-colorful-ball': draw1DColorfulBall, 53 | '1d-radius-ball': draw1DRadiusBall, 54 | '1d-colorful-triangle': draw1DColorfulTriangle, 55 | '2d-grayscale-image': draw2DGrayscaleImage, 56 | '2d-colorful-image': draw2DColorfulImage, 57 | '2d-marble-image': draw2DMarbleImage, 58 | '2d-colorful-marble-image': draw2DColorfulMarbleImage, 59 | '2d-wood-image': draw2DWoodImage, 60 | '2d-colorful-wood-image': draw2DColorfulWoodImage, 61 | '2d-terrain': draw2DTerrainImage 62 | }; 63 | 64 | const methodFnsMap = { 65 | 'white-noise': WhiteNoise, 66 | 'value-noise-2d': ValueNoise2D, 67 | 'perlin-noise-2d': PerlinNoise2D, 68 | }; 69 | 70 | const filterFnsMap = { 71 | 'none': id, 72 | 'cosine': cosFilter, 73 | 'smoothstep': smoothStepFilter, 74 | 'fade': fadeFilter, 75 | }; 76 | 77 | const outputFilterFnsMap = { 78 | 'none': id, 79 | 'turbulance': turbulenceOFilter, 80 | }; 81 | 82 | const visualisationDefaults = { 83 | '1d-line': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 84 | '1d-colorful-line': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 85 | '1d-color-gradient': {speed: 30, amplitude: 500, frequency: 0.0003, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 86 | '1d-rgb-lines': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 87 | '1d-ball': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 88 | '1d-colorful-ball': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 89 | '1d-radius-ball': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 90 | '1d-colorful-triangle': {speed: 30, amplitude: 500, frequency: 0.001, octave: 1, lacunarity: 1, gain:1, method:'value-noise-2d', syncOffsetsXY: false, stop: false}, 91 | '2d-grayscale-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.02, octave: 5, lacunarity: 2, gain:0.5, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 92 | '2d-colorful-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.02, octave: 5, lacunarity: 2, gain:0.5, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 93 | '2d-marble-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.02, octave: 5, lacunarity: 2, gain:0.5, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 94 | '2d-colorful-marble-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.02, octave: 5, lacunarity: 2, gain:0.5, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 95 | '2d-wood-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.008, octave: 1, lacunarity: 1, gain:1, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 96 | '2d-colorful-wood-image': {speed: 0, offset: 0, amplitude: 1.8, frequency: 0.008, octave: 1, lacunarity: 1, gain:1, method:'perlin-noise-2d', filter:'fade', disable: [playPauseButton, speedInput], syncOffsetsXY: true, stop: true}, 97 | '2d-terrain': {speed: 10, offset: 0, amplitude: 0.95, frequency: 0.01, octave: 5, lacunarity: 2, gain:0.5, method:'perlin-noise-2d', filter:'fade', syncOffsetsXY: true, stop: false}, 98 | }; 99 | 100 | Object.keys(visualisationDefaults).map(key => visualisationDefaults[key].visualisation = key); 101 | 102 | const presets = { 103 | 'dancing-mountains': {visualisation: '1d-line', method: 'value-noise-2d', filter: 'smoothstep', outputFilter: 'none', speed: 1, amplitude: 500, frequency: 0.0005, octave: 5, lacunarity: 3, gain:1, syncOffsetsXY:true, stop:false}, 104 | 'rainy-clouds': {visualisation: '2d-grayscale-image', method: 'value-noise-2d', filter: 'smoothstep', outputFilter: 'none', speed: 0, offset: 0, amplitude: 1.8, frequency: 0.002, octave: 5, lacunarity: 2, gain:0.5, stop:true}, 105 | 'dream-clouds': {visualisation: '2d-colorful-image', method: 'value-noise-2d', filter: 'smoothstep', outputFilter: 'none', speed: 0, offset: 0, amplitude: 1.8, frequency: 0.002, octave: 5, lacunarity: 2, gain:0.5, stop:true}, 106 | } 107 | 108 | Object.keys(presets).map(key => presets[key].disable = visualisationDefaults[presets[key].visualisation].disable); 109 | 110 | function setOption(el, val) { 111 | el.selectedIndex = [...el.options].find(({value}) => value === val).index; 112 | } 113 | 114 | function applySettings(settings) { 115 | method = first(settings.method, method); 116 | filter = first(settings.filter, filter); 117 | outputFilter = first(settings.outputFilter, outputFilter); 118 | visualisation = first(settings.visualisation, visualisation); 119 | syncOffsetsXY = first(settings.syncOffsetsXY, syncOffsetsXY); 120 | 121 | if (applyDefaults) { 122 | speed = first(settings.speed, speed); 123 | offset = first(settings.offset, offset) 124 | frequency = first(settings.frequency, frequency) 125 | amplitude = first(settings.amplitude, amplitude) 126 | octave = first(settings.octave, octave) 127 | lacunarity = first(settings.lacunarity, lacunarity) 128 | gain = first(settings.gain, gain) 129 | } 130 | 131 | elements.forEach(element => element.disabled = false); 132 | 133 | speedInput.value = speed; 134 | offsetInput.value = offset; 135 | frequencyInput.value = frequency; 136 | amplitudeInput.value = amplitude; 137 | octaveInput.value = octave; 138 | lacunarityInput.value = lacunarity; 139 | gainInput.value = gain; 140 | seedInput.value = seed; 141 | syncOffsetsInput.checked = syncOffsetsXY; 142 | 143 | setOption(visualisationSelect, visualisation); 144 | setOption(methodSelect, method); 145 | setOption(filterSelect, filter); 146 | setOption(outputFilterSelect, outputFilter); 147 | 148 | (settings.disable || []).forEach(element => element.disabled = true); 149 | 150 | stopped = first(settings.stop, stopped); 151 | draw(!stopped); 152 | } 153 | 154 | function getCurrentState() { 155 | const visualisationFn = visualisationFnsMap[visualisation]; 156 | const methodFn = methodFnsMap[method]; 157 | const filterFn = filterFnsMap[filter]; 158 | const outputFilterFn = outputFilterFnsMap[outputFilter]; 159 | 160 | return { 161 | visualisationFn, 162 | methodFn, 163 | filterFn, 164 | outputFilterFn 165 | } 166 | } 167 | 168 | function on(element, eventName, fn) { 169 | element.addEventListener(eventName, e => { 170 | if (element !== presetSelect) { 171 | setOption(presetSelect, 'none'); 172 | } 173 | 174 | fn(e); 175 | }); 176 | } 177 | 178 | function pauseOnFocus(element) { 179 | let prevPaused; 180 | on(element, 'focus', e => {prevPaused = paused; paused = true}); 181 | on(element, 'blur', e => {paused = prevPaused}); 182 | } 183 | 184 | function onChange(element, fn) { 185 | on(element, 'change', e => fn(e.target.value)); 186 | } 187 | 188 | function onCheck(element, fn) { 189 | on(element, 'change', e => fn(e.target.checked)); 190 | } 191 | 192 | function onChangeGetFloat(element, fn) { 193 | onChange(element, val => fn(parseFloat(val, 10))); 194 | } 195 | 196 | function onChangeGetInt(element, fn) { 197 | onChange(element, val => fn(parseInt(val, 10))); 198 | } 199 | 200 | onChange(presetSelect, presetName => { 201 | applySettings(presets[presetName]); 202 | }); 203 | 204 | onChange(visualisationSelect, val => { 205 | applySettings(visualisationDefaults[val]); 206 | }); 207 | 208 | onChange(methodSelect, val => method = val); 209 | onChange(filterSelect, val => filter = val); 210 | onChange(outputFilterSelect, val => outputFilter = val); 211 | 212 | onChangeGetFloat(speedInput, val => speed = val); 213 | onChangeGetFloat(offsetInput, val => offset = val); 214 | onChangeGetFloat(frequencyInput, val => frequency = val); 215 | onChangeGetFloat(amplitudeInput, val => amplitude = val); 216 | onChangeGetFloat(octaveInput, val => octave = val); 217 | onChangeGetFloat(lacunarityInput, val => lacunarity = val); 218 | onChangeGetFloat(gainInput, val => gain = val); 219 | pauseOnFocus(offsetInput); 220 | 221 | onChangeGetInt(seedInput, val => { 222 | seed = val; 223 | setRandomSeed(val); 224 | 225 | ValueNoise2D({}); 226 | PerlinNoise2D({}); 227 | 228 | offset = 0; 229 | }); 230 | 231 | onCheck(leaveTailInput, val => leaveTail = val); 232 | onCheck(applyDefaultsInput, val => applyDefaults = val); 233 | onCheck(syncOffsetsInput, val => syncOffsetsXY = val); 234 | 235 | on(drawButton, 'click', () => draw(false)); 236 | on(playPauseButton, 'click', () => { 237 | paused = !paused; 238 | playPauseButton.innerText = paused ? 'Start Auto Draw' : 'Auto Drawing'; 239 | playPauseButton.style.color = paused ? '#e0e0e0' : 'yellowgreen'; 240 | }); 241 | on(incrementOffsetButton, 'click', () => { 242 | offset += speed; 243 | offsetInput.value = parseFloat(offset.toFixed('10')); 244 | }); 245 | on(resetOffsetButton, 'click', () => offset = 0); -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | border: none; 5 | outline: none; 6 | } 7 | 8 | html, body { 9 | width: 100%; 10 | height: 100%; 11 | overflow: hidden; 12 | background-color: #000; 13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 14 | color: #e0e0e0; 15 | } 16 | 17 | .flex-row { 18 | display: flex; 19 | flex-direction: row; 20 | align-items: center; 21 | justify-content: flex-start; 22 | } 23 | 24 | .pointer-cursor { 25 | cursor: pointer 26 | } 27 | 28 | .root { 29 | position: absolute; 30 | z-index: 2; 31 | top: 0; 32 | left: 0; 33 | right: 0; 34 | bottom: 0; 35 | } 36 | 37 | .panel { 38 | width: 211px; 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | bottom: 0; 43 | display: flex; 44 | flex-direction: column; 45 | background: #111; 46 | box-sizing: border-box; 47 | overflow: hidden; 48 | border-right: 1px solid #333; 49 | } 50 | 51 | .panel-header { 52 | padding: 20px 20px 10px 20px; 53 | } 54 | 55 | .panel-body { 56 | flex: 1; 57 | padding: 0 20px 20px 20px; 58 | overflow: auto; 59 | } 60 | 61 | .panel-footer { 62 | width: 210px; 63 | height: 41px; 64 | padding: 10px 20px; 65 | background: #111; 66 | box-sizing: border-box; 67 | display: flex; 68 | justify-content: space-between; 69 | border-top: 1px solid #333; 70 | box-shadow: -10px -5px 18px 1px #000; 71 | } 72 | 73 | .page-title { 74 | font-size: 52px; 75 | text-align: center; 76 | color: #333; 77 | line-height: 47px; 78 | } 79 | 80 | .input-wrapper { 81 | padding: 10px 0; 82 | } 83 | 84 | .input-checkbox-wrapper { 85 | padding: 2px 0; 86 | } 87 | 88 | .input-name, 89 | .input-label { 90 | color: #999; 91 | font-size: 14px; 92 | } 93 | 94 | .input-name { 95 | margin-bottom: 4px; 96 | } 97 | 98 | .input-label { 99 | flex: 1; 100 | } 101 | 102 | .input-content { 103 | } 104 | 105 | .input-element { 106 | padding: 5px 10px; 107 | border-radius: 3px; 108 | font-size: 14px; 109 | width: 100%; 110 | box-sizing: border-box; 111 | background: #333; 112 | color: #e0e0e0; 113 | } 114 | 115 | .input-select { 116 | -webkit-appearance: none; 117 | appearance: none; 118 | } 119 | 120 | .input-button { 121 | margin-bottom: 2px; 122 | } 123 | 124 | .input-button:hover { 125 | opacity: 0.9; 126 | cursor: pointer; 127 | } 128 | 129 | .input-checkbox { 130 | width: 25px; 131 | } 132 | 133 | .input-element:disabled, 134 | .input-element:disabled:hover { 135 | opacity: 0.5; 136 | cursor: not-allowed; 137 | } -------------------------------------------------------------------------------- /utils/draw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Draw circle 3 | */ 4 | function circle(x, y, r, options) { 5 | const { 6 | color = '#fff', 7 | fill = true, 8 | startAngle = 0, 9 | endAngle = TWO_PI, 10 | aCW = true, 11 | lineWidth = 1, 12 | } = (options || {}); 13 | 14 | context.lineWidth = lineWidth; 15 | context.strokeStyle = color; 16 | context.fillStyle = color; 17 | context.moveTo(x, y); 18 | context.beginPath(); 19 | 20 | fill && context.moveTo(x, y); 21 | 22 | context.arc(x, y, r, startAngle, endAngle, aCW); 23 | 24 | fill && context.closePath(); 25 | fill ? context.fill() : context.stroke(); 26 | }; 27 | 28 | /** 29 | * Draw line 30 | */ 31 | function line(fromX, fromY, toX, toY, options) { 32 | const { 33 | color = '#fff', 34 | lineWidth = 1, 35 | } = (options || {}); 36 | 37 | context.setLineDash([]); 38 | context.lineWidth = lineWidth; 39 | context.strokeStyle = color; 40 | context.beginPath(); 41 | context.moveTo(fromX, fromY); 42 | context.lineTo(toX, toY); 43 | context.stroke(); 44 | } 45 | 46 | /** 47 | * Draw dashed line 48 | */ 49 | function dashedLine(xFrom, yFrom, xTo, yTo, options) { 50 | const { 51 | dashLen = 10, 52 | sepLen = 7, 53 | color = '#fff', 54 | lineWidth = 1 55 | } = (options || {}); 56 | 57 | context.lineWidth = lineWidth; 58 | context.strokeStyle = color; 59 | 60 | context.beginPath(); 61 | context.setLineDash([dashLen, sepLen]); 62 | context.moveTo(xFrom, yFrom); 63 | context.lineTo(xTo, yTo); 64 | context.stroke(); 65 | context.setLineDash([]); 66 | } 67 | 68 | /** 69 | * Draw rectangle 70 | */ 71 | function rect(x, y, w, h, options) { 72 | const { 73 | color = '#fff', 74 | fill = true, 75 | lineWidth = 1, 76 | } = (options || {}); 77 | 78 | context.beginPath(); 79 | context.lineWidth = lineWidth, 80 | context.strokeStyle = color; 81 | context.fillStyle = color; 82 | context.rect(x, y, w, h); 83 | context.closePath(); 84 | fill ? context.fill() : context.stroke(); 85 | }; 86 | 87 | /** 88 | * Draw text. 89 | */ 90 | const text = (x, y, text, options = {}) => { 91 | const { 92 | color = '#fff', 93 | size = 14, 94 | align = 'center', 95 | baseline = 'bottom', 96 | } = options; 97 | 98 | context.font = `${size}px sans-serif`; 99 | context.textBaseline = baseline; 100 | context.textAlign = align; 101 | context.fillStyle = color; 102 | context.fillText(text, x, y); 103 | }; -------------------------------------------------------------------------------- /utils/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Builtins 3 | */ 4 | const max = Math.max; 5 | const min = Math.min; 6 | const ceil = Math.ceil; 7 | const floor = Math.floor; 8 | const round = Math.round; 9 | const cos = Math.cos; 10 | const pow = Math.pow; 11 | const abs = Math.abs; 12 | const sin = Math.sin; 13 | 14 | /** 15 | * Constants 16 | */ 17 | const PI = Math.PI; 18 | const TWO_PI = PI * 2; 19 | 20 | /** 21 | * Linear Congruential Generator 22 | */ 23 | const LCGMultiplier = 1664525; 24 | const LCGIncrement = 1013904223; 25 | const LCGModulus = pow(2, 32); 26 | 27 | let LCGSeed = Date.now(); 28 | function setRandomSeed(seed) { 29 | LCGSeed = seed; 30 | } 31 | 32 | function srandInt() { 33 | return LCGSeed = (LCGMultiplier * LCGSeed + LCGIncrement) % LCGModulus; 34 | } 35 | 36 | function srand() { 37 | return srandInt() / LCGModulus; 38 | } 39 | 40 | function srandRange(min, max) { 41 | return srand() * (max - min) + min; 42 | } 43 | 44 | function srandRangeInt(min, max) { 45 | return round(srandRange(min,max)); 46 | } 47 | 48 | /** 49 | * Filter methods 50 | */ 51 | function fadeFilter(t) { 52 | return t * t * t * (t * (t * 6 - 15) + 10); 53 | } 54 | 55 | function cosFilter(t) { 56 | return (1 - cos(t * PI)) * 0.5; 57 | } 58 | 59 | function smoothStepFilter(t) { 60 | return t * t * (3 - 2 * t); 61 | } 62 | 63 | /** 64 | * Output filter methods. 65 | */ 66 | function turbulenceOFilter(t) { 67 | return abs((2 * t) - 1); 68 | } 69 | 70 | /** 71 | * Interpolation 72 | */ 73 | function lerp(a, b, t) { 74 | return a*(1-t) + b*t; 75 | } 76 | 77 | function bilerp(c00, c10, c01, c11, tx, ty) { 78 | return lerp( 79 | lerp(c00, c10, tx), 80 | lerp(c01, c11, tx), 81 | ty 82 | ); 83 | } 84 | 85 | /** 86 | * Map number n which between the range a..b to another range x..y 87 | */ 88 | function map(currFrom, currTo, targetFrom, targetTo, n) { 89 | return ((n - currFrom) / (currTo - currFrom) * (targetTo - targetFrom)) + targetFrom; 90 | } 91 | 92 | /** 93 | * Constrain number between min max. 94 | */ 95 | function constrain(low, high, n) { 96 | return max(min(n, high), low); 97 | } -------------------------------------------------------------------------------- /utils/misc.js: -------------------------------------------------------------------------------- 1 | function id(t) { 2 | return t; 3 | } 4 | 5 | function first(...args) { 6 | for (let i = 0; i < args.length; i++) { 7 | if (typeof args[i] !== 'undefined') { 8 | return args[i]; 9 | } 10 | } 11 | } 12 | 13 | function flat(arr) { 14 | return arr.flat ? arr.flat() : arr.reduce((a, b) => a.concat(b), []); 15 | } -------------------------------------------------------------------------------- /utils/worker.js: -------------------------------------------------------------------------------- 1 | function run(fn, globals, done) { 2 | const now = debugMode ? performance.now() : 0; 3 | const worker = new Worker(URL.createObjectURL(new Blob([` 4 | // Utils 5 | const PI = Math.PI; 6 | const TWO_PI = PI * 2; 7 | const max = Math.max; 8 | const min = Math.min; 9 | const ceil = Math.ceil; 10 | const floor = Math.floor; 11 | const round = Math.round; 12 | const cos = Math.cos; 13 | const pow = Math.pow; 14 | const abs = Math.abs; 15 | const sin = Math.sin; 16 | const LCGMultiplier = ${LCGMultiplier}; 17 | const LCGIncrement = ${LCGIncrement}; 18 | const LCGModulus = ${LCGModulus}; 19 | let LCGSeed = ${LCGSeed}; 20 | ${setRandomSeed}; 21 | ${srandInt}; 22 | ${srand}; 23 | ${srandRange}; 24 | ${fadeFilter}; 25 | ${cosFilter}; 26 | ${smoothStepFilter}; 27 | ${turbulenceOFilter}; 28 | ${lerp}; 29 | ${bilerp}; 30 | ${map}; 31 | ${constrain}; 32 | ${id}; 33 | ${first}; 34 | ${flat}; 35 | 36 | // Globals 37 | let speed = ${speed}; 38 | let offset = ${offset}; 39 | let amplitude = ${amplitude}; 40 | let frequency = ${frequency}; 41 | let octave = ${octave}; 42 | let lacunarity = ${lacunarity}; 43 | let gain = ${gain}; 44 | let seed = ${seed}; 45 | let syncOffsetsXY = ${syncOffsetsXY}; 46 | 47 | // Value Noise 2D 48 | const VALUE_NOISE_2D_DEFAULT_MAX_VERTICES = ${VALUE_NOISE_2D_DEFAULT_MAX_VERTICES}; 49 | const VALUE_NOISE_2D_DEFAULT_MAX_VERTICES_MASK = ${VALUE_NOISE_2D_DEFAULT_MAX_VERTICES_MASK}; 50 | const ValueNoise2DPermutationTable = ${JSON.stringify(ValueNoise2DPermutationTable)}; 51 | const ValueNoise2DRandomsBySeed = ${JSON.stringify(ValueNoise2DRandomsBySeed)}; 52 | ${ValueNoise2D}; 53 | 54 | // Perlin Noise 2D 55 | const PERLIN_NOISE_2D_DEFAULT_MAX_VERTICES = ${PERLIN_NOISE_2D_DEFAULT_MAX_VERTICES}; 56 | const PERLIN_NOISE_2D_DEFAULT_MAX_VERTICES_MASK = ${PERLIN_NOISE_2D_DEFAULT_MAX_VERTICES_MASK}; 57 | let PerlinNoise2DPermutationTableBase = ${JSON.stringify(PerlinNoise2DPermutationTableBase)}; 58 | let PerlinNoise2DPermutationTableBySeed = ${JSON.stringify(PerlinNoise2DPermutationTableBySeed)}; 59 | ${getPerlinNoise2dPermutationTableBySeed}; 60 | ${Perlin2DGradientDotProd}; 61 | ${PerlinNoise2D}; 62 | 63 | // State 64 | const visualisationFn = ${getCurrentState().visualisationFn}; 65 | const methodFn = ${getCurrentState().methodFn}; 66 | const filterFn = ${getCurrentState().filterFn}; 67 | const outputFilterFn = ${getCurrentState().outputFilterFn}; 68 | 69 | // Initialize 70 | setRandomSeed(seed); 71 | 72 | // Supplied Globals 73 | ${Object.keys(globals).map(name => `const ${name} = ${globals[name]};`).join('\n')} 74 | 75 | // Run fn. 76 | postMessage((${fn})()); 77 | `]))); 78 | debugMode && console.log(`Worker prepared, took ${performance.now() - now}ms.`); 79 | 80 | worker.onmessage = ({data}) => { 81 | debugMode && console.log(`Worker job completed, took ${performance.now() - now}ms.`); 82 | worker.terminate(); 83 | done(data); 84 | }; 85 | } -------------------------------------------------------------------------------- /visualisations/1d-ball.js: -------------------------------------------------------------------------------- 1 | function draw1DBall({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height, 7 | color = '#f0f0f0', 8 | }) { 9 | context.clearRect(offsetX, height-1, offsetX + width, 3); 10 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 11 | 12 | const y = getValueFn(0); 13 | 14 | const radius = 20; 15 | const xPos = offsetX + (width / 2); 16 | const yPos = offsetY + height - radius/2 - y; 17 | 18 | circle(xPos, yPos, radius, {color}); 19 | } -------------------------------------------------------------------------------- /visualisations/1d-color-gradient.js: -------------------------------------------------------------------------------- 1 | function draw1DColorGradient({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | for (let x = 0; x < width - 100; x++) { 9 | const xPos = offsetX + x; 10 | 11 | const r = floor(255 * getValueFn(x + 2/frequency) / amplitude); 12 | const g = floor(255 * getValueFn(x + 64/frequency) / amplitude); 13 | const b = floor(255 * getValueFn(x + 128/frequency) / amplitude); 14 | 15 | line(xPos + 50, offsetY + 50, xPos + 50, offsetY + 150, {color: `rgb(${r}, ${g}, ${b})`}); 16 | } 17 | } -------------------------------------------------------------------------------- /visualisations/1d-colorful-ball.js: -------------------------------------------------------------------------------- 1 | function draw1DColorfulBall({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height, 7 | color = '#f0f0f0', 8 | }) { 9 | context.clearRect(offsetX, height-1, offsetX + width, 3); 10 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 11 | 12 | const y = getValueFn(0); 13 | 14 | const radius = 20; 15 | const xPos = offsetX + (width / 2); 16 | const yPos = offsetY + height - radius/2 - y; 17 | 18 | const r = floor(255 * getValueFn(y + 2/frequency) / amplitude); 19 | const g = floor(255 * getValueFn(y + 64/frequency) / amplitude); 20 | const b = floor(255 * getValueFn(y + 128/frequency / amplitude)); 21 | 22 | circle(xPos, yPos, radius, {color: `rgb(${r}, ${g}, ${b})`}); 23 | } -------------------------------------------------------------------------------- /visualisations/1d-colorful-line.js: -------------------------------------------------------------------------------- 1 | function draw1DColorfulLine({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | context.clearRect(offsetX, height-1, offsetX + width, 3); 9 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 10 | 11 | for (let x = 0; x < width; x++) { 12 | const y = getValueFn(x); 13 | 14 | const xPos = offsetX + x; 15 | const yPos = offsetY + height - y; 16 | 17 | const r = floor(255 * getValueFn(x + 2/frequency)) / amplitude; 18 | const g = floor(255 * getValueFn(x + 64/frequency)) / amplitude; 19 | const b = floor(255 * getValueFn(x + 128/frequency)) / amplitude; 20 | 21 | line(xPos, yPos, xPos, yPos - 1, {color: `rgb(${r}, ${g}, ${b})`}); 22 | } 23 | } -------------------------------------------------------------------------------- /visualisations/1d-colorful-triangle.js: -------------------------------------------------------------------------------- 1 | function draw1DColorfulTriangle({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | const a = getValueFn(offsetX + 2/frequency) / amplitude; 9 | const b = getValueFn(offsetX + 64/frequency) / amplitude; 10 | const c = getValueFn(offsetX + 128/frequency) / amplitude; 11 | 12 | const x1 = offsetX + floor(width * a); 13 | const y1 = offsetY + floor(height * b); 14 | 15 | const x2 = offsetX + floor(width * b); 16 | const y2 = offsetY + floor(height * c); 17 | 18 | const x3 = offsetX + floor(width * c); 19 | const y3 = offsetY + floor(height * a); 20 | 21 | // the triangle 22 | context.beginPath(); 23 | context.moveTo(x1, y1); 24 | context.lineTo(x2, y2); 25 | context.lineTo(x3, y3); 26 | context.closePath(); 27 | 28 | // the outline 29 | context.lineWidth = 3; 30 | context.strokeStyle = '#fff'; 31 | context.stroke(); 32 | 33 | // the fill color 34 | context.fillStyle = `rgb( 35 | ${map(0, 1, 0, 255, a)}, 36 | ${map(0, 1, 0, 255, b)}, 37 | ${map(0, 1, 0, 255, c)} 38 | )`; 39 | context.fill(); 40 | 41 | circle(x1, y1, 10, {color:'#f00'}); 42 | circle(x2, y2, 10, {color:'#0f0'}); 43 | circle(x3, y3, 10, {color:'#00f'}); 44 | } -------------------------------------------------------------------------------- /visualisations/1d-line.js: -------------------------------------------------------------------------------- 1 | function draw1DLine({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height, 7 | color = '#f0f0f0', 8 | }) { 9 | context.clearRect(offsetX, height-1, offsetX + width, 3); 10 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 11 | 12 | for (let x = 0; x < width; x++) { 13 | const y = getValueFn(x); 14 | 15 | const xPos = offsetX + x; 16 | const yPos = offsetY + height - y; 17 | 18 | line(xPos, yPos, xPos, yPos - 1, {color}); 19 | } 20 | } -------------------------------------------------------------------------------- /visualisations/1d-radius-ball.js: -------------------------------------------------------------------------------- 1 | function draw1DRadiusBall({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height, 7 | color = '#f0f0f0', 8 | }) { 9 | context.clearRect(offsetX, height-1, offsetX + width, 3); 10 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 11 | 12 | const y = getValueFn(0) / amplitude; 13 | 14 | const maxRadius = 200; 15 | const radius = y * maxRadius; 16 | const xPos = offsetX + width/2; 17 | const yPos = offsetY + height/2; 18 | 19 | circle(xPos, yPos, radius, {color}); 20 | } -------------------------------------------------------------------------------- /visualisations/1d-rgb-lines.js: -------------------------------------------------------------------------------- 1 | function draw1DRGBLines({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | context.clearRect(offsetX, height-1, offsetX + width, 3); 9 | dashedLine(offsetX, height, offsetX + width, height, {color:'#333'}); 10 | 11 | for (let x = 0; x < width; x++) { 12 | const xPos = offsetX + x; 13 | const yPosBase = offsetY + height; 14 | 15 | const rV = getValueFn(x + 2/frequency) / amplitude; 16 | const gV = getValueFn(x + 64/frequency) / amplitude; 17 | const bV = getValueFn(x + 128/frequency) / amplitude; 18 | 19 | const r = floor(255 * rV); 20 | const g = floor(255 * gV); 21 | const b = floor(255 * bV); 22 | 23 | const rY = rV * amplitude; 24 | const gY = gV * amplitude; 25 | const bY = bV * amplitude; 26 | 27 | line(xPos, 100, xPos, 101, {color: `rgb(${r}, ${g}, ${b})`}); 28 | line(xPos, yPosBase - rY, xPos, yPosBase - rY - 1, {color: `rgb(${r}, ${0}, ${0})`}); 29 | line(xPos, yPosBase - gY, xPos, yPosBase - gY - 1, {color: `rgb(${0}, ${g}, ${0})`}); 30 | line(xPos, yPosBase - bY, xPos, yPosBase - bY - 1, {color: `rgb(${0}, ${0}, ${b})`}); 31 | } 32 | } -------------------------------------------------------------------------------- /visualisations/2d-colorful-image.js: -------------------------------------------------------------------------------- 1 | function draw2DColorfulImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | let maxValue = -Infinity; 20 | let minValue = Infinity; 21 | 22 | for (let y = 0; y < height; y++) { 23 | for (let x = 0; x < width; x++) { 24 | const r = map(0, amplitude, 0, 255, getValueFn(x + 2/frequency, y + 2/frequency)); 25 | const g = map(0, amplitude, 0, 255, getValueFn(x + 64/frequency, y + 64/frequency)); 26 | const b = map(0, amplitude, 0, 255, getValueFn(x + 128/frequency, y + 128/frequency)); 27 | 28 | maxValue = max(maxValue, r, g, b); 29 | minValue = min(minValue, r, g, b); 30 | 31 | result.push([xPosBase + x, yPosBase - y, r, g, b]); 32 | } 33 | } 34 | 35 | result.forEach(r => { 36 | r[2] = map(minValue, maxValue, 0, 255, r[2]); 37 | r[3] = map(minValue, maxValue, 0, 255, r[3]); 38 | r[4] = map(minValue, maxValue, 0, 255, r[4]); 39 | }); 40 | 41 | return result; 42 | } 43 | 44 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 45 | context.clearRect(0, 0, screenWidth, screenHeight); 46 | 47 | result.forEach(([xPos, yPos, r, g, b]) => { 48 | context.fillStyle = `rgb(${r}, ${g}, ${b})`; 49 | context.fillRect(xPos, yPos, 1, 1); 50 | }); 51 | }); 52 | } -------------------------------------------------------------------------------- /visualisations/2d-colorful-marble-image.js: -------------------------------------------------------------------------------- 1 | function draw2DColorfulMarbleImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | let maxValue = -Infinity; 20 | let minValue = Infinity; 21 | 22 | for (let y = 0; y < height; y++) { 23 | for (let x = 0; x < width; x++) { 24 | const r = map(0, amplitude, 0, 255, getValueFn(x + 2/frequency, y + 2/frequency)); 25 | const g = map(0, amplitude, 0, 255, getValueFn(x + 64/frequency, y + 64/frequency)); 26 | const b = map(0, amplitude, 0, 255, getValueFn(x + 128/frequency, y + 128/frequency)); 27 | 28 | maxValue = max(maxValue, r, g, b); 29 | minValue = min(minValue, r, g, b); 30 | 31 | result.push([xPosBase + x, yPosBase - y, x, r, g, b]); 32 | } 33 | } 34 | 35 | result.forEach(rs => { 36 | const [,,x,rV,gV,bV] = rs; 37 | 38 | const rI = map(minValue, maxValue, 0, 1, rV); 39 | const gI = map(minValue, maxValue, 0, 1, gV); 40 | const bI = map(minValue, maxValue, 0, 1, bV); 41 | 42 | const r = (sin((x + rI * 100) * 2 * PI / 200) + 1) / 2; 43 | const g = (sin((x + gI * 100) * 2 * PI / 200) + 1) / 2; 44 | const b = (sin((x + bI * 100) * 2 * PI / 200) + 1) / 2; 45 | 46 | rs[3] = map(0, 1, 0, 255, r); 47 | rs[4] = map(0, 1, 0, 255, g); 48 | rs[5] = map(0, 1, 0, 255, b); 49 | }); 50 | 51 | return result; 52 | } 53 | 54 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 55 | context.clearRect(0, 0, screenWidth, screenHeight); 56 | 57 | result.forEach(([xPos, yPos, , r, g, b]) => { 58 | context.fillStyle = `rgb(${r}, ${g}, ${b})`; 59 | context.fillRect(xPos, yPos, 1, 1); 60 | }); 61 | }); 62 | } -------------------------------------------------------------------------------- /visualisations/2d-colorful-wood-image.js: -------------------------------------------------------------------------------- 1 | function draw2DColorfulWoodImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | let maxValue = -Infinity; 20 | let minValue = Infinity; 21 | 22 | for (let y = 0; y < height; y++) { 23 | for (let x = 0; x < width; x++) { 24 | const r = map(0, amplitude, 0, 255, getValueFn(x + 2/frequency, y + 2/frequency)); 25 | const g = map(0, amplitude, 0, 255, getValueFn(x + 64/frequency, y + 64/frequency)); 26 | const b = map(0, amplitude, 0, 255, getValueFn(x + 128/frequency, y + 128/frequency)); 27 | 28 | maxValue = max(maxValue, r, g, b); 29 | minValue = min(minValue, r, g, b); 30 | 31 | result.push([xPosBase + x, yPosBase - y, r, g, b]); 32 | } 33 | } 34 | 35 | result.forEach(r => { 36 | const gR = map(minValue, maxValue, 0, 1, r[2]) * 10; 37 | const gG = map(minValue, maxValue, 0, 1, r[3]) * 10; 38 | const gB = map(minValue, maxValue, 0, 1, r[4]) * 10; 39 | 40 | r[2] = map(0, 1, 0, 255, gR - floor(gR)); 41 | r[3] = map(0, 1, 0, 255, gG - floor(gG)); 42 | r[4] = map(0, 1, 0, 255, gB - floor(gB)); 43 | }); 44 | 45 | return result; 46 | } 47 | 48 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 49 | context.clearRect(0, 0, screenWidth, screenHeight); 50 | 51 | result.forEach(([xPos, yPos, r, g, b]) => { 52 | context.fillStyle = `rgb(${r}, ${g}, ${b})`; 53 | context.fillRect(xPos, yPos, 1, 1); 54 | }); 55 | }); 56 | } -------------------------------------------------------------------------------- /visualisations/2d-grayscale-image.js: -------------------------------------------------------------------------------- 1 | function draw2DGrayscaleImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | let maxValue = -Infinity; 20 | let minValue = Infinity; 21 | 22 | for (let y = 0; y < height; y++) { 23 | for (let x = 0; x < width; x++) { 24 | const value = getValueFn(x, y); 25 | 26 | maxValue = max(maxValue, value); 27 | minValue = min(minValue, value); 28 | 29 | result.push([xPosBase + x, yPosBase - y, value]); 30 | } 31 | } 32 | 33 | result.forEach(r => { 34 | r[2] = map(minValue, maxValue, 0, 255, r[2]); 35 | }); 36 | 37 | return result; 38 | } 39 | 40 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 41 | context.clearRect(0, 0, screenWidth, screenHeight); 42 | 43 | result.forEach(([xPos, yPos, value]) => { 44 | context.fillStyle = `rgb(${value}, ${value}, ${value})`; 45 | context.fillRect(xPos, yPos, 1, 1); 46 | }); 47 | }); 48 | } -------------------------------------------------------------------------------- /visualisations/2d-marble-image.js: -------------------------------------------------------------------------------- 1 | function draw2DMarbleImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | 17 | for (let y = 0; y < height; y++) { 18 | for (let x = 0; x < width; x++) { 19 | const value = multiply(getValueFn(x, y)[0], octave); 20 | 21 | const xPos = offsetX + x + 100; 22 | const yPos = offsetY + height + 100 - y; 23 | 24 | result.push({xPos, yPos, value, x}); 25 | } 26 | } 27 | 28 | return result; 29 | } 30 | 31 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 32 | context.clearRect(0, 0, screenWidth, screenHeight); 33 | 34 | let maxValue = 0; 35 | 36 | result.forEach(({value}) => { 37 | maxValue = Math.max(maxValue, value); 38 | }); 39 | 40 | result.forEach(({xPos, yPos, value, x}) => { 41 | const i = map(0, maxValue, 0, 1, value); 42 | const val = (sin((x + i * 100) * 2 * PI / 200) + 1) / 2; 43 | const bw = map(0, 1, 0, 255, val); 44 | 45 | context.fillStyle = `rgb(${bw}, ${bw}, ${bw})`; 46 | context.fillRect(xPos, yPos, 1, 1); 47 | }); 48 | }); 49 | } 50 | 51 | function draw2DMarbleImage({ 52 | getValueFn, 53 | offsetX, 54 | offsetY, 55 | width, 56 | height 57 | }) { 58 | height -= 100; 59 | width -= 200; 60 | 61 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 62 | 63 | const calculate = () => { 64 | const result = []; 65 | const xPosBase = offsetX + 100; 66 | const yPosBase = offsetY + height + 100; 67 | 68 | let maxValue = -Infinity; 69 | let minValue = Infinity; 70 | 71 | for (let y = 0; y < height; y++) { 72 | for (let x = 0; x < width; x++) { 73 | const value = getValueFn(x, y); 74 | 75 | maxValue = max(maxValue, value); 76 | minValue = min(minValue, value); 77 | 78 | result.push([xPosBase + x, yPosBase - y, x, value]); 79 | } 80 | } 81 | 82 | result.forEach(r => { 83 | const [,,x,value] = r; 84 | const i = map(minValue, maxValue, 0, 1, value); 85 | const val = (sin((x + i * 100) * 2 * PI / 200) + 1) / 2; 86 | r[3] = map(0, 1, 0, 255, val); 87 | }); 88 | 89 | return result; 90 | } 91 | 92 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 93 | context.clearRect(0, 0, screenWidth, screenHeight); 94 | 95 | result.forEach(([xPos, yPos, , value]) => { 96 | context.fillStyle = `rgb(${value}, ${value}, ${value})`; 97 | context.fillRect(xPos, yPos, 1, 1); 98 | }); 99 | }); 100 | } -------------------------------------------------------------------------------- /visualisations/2d-terrain.js: -------------------------------------------------------------------------------- 1 | function draw2DTerrainImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height, 7 | done 8 | }) { 9 | height -= 100; 10 | width -= 200; 11 | 12 | const tileSize = 4; 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | for (let y = 0; y < height/tileSize; y++) { 20 | for (let x = 0; x < width/tileSize; x++) { 21 | const value = getValueFn(x, y); 22 | 23 | result.push([ 24 | xPosBase + (x * tileSize), 25 | yPosBase - (y * tileSize), 26 | value 27 | ]); 28 | } 29 | } 30 | 31 | return result; 32 | } 33 | 34 | run(calculate, {getValueFn, height, width, offsetX, offsetY, tileSize}, result => { 35 | context.clearRect(0, 0, screenWidth, screenHeight); 36 | 37 | result.forEach(([xPos, yPos, value]) => { 38 | let color; 39 | 40 | if (value < 0.4) { color = '#05B2DC'; } // sea 41 | else if (value < 0.42) { color = '#FFC857'; } // sand 42 | else if (value < 0.54) { color = '#52AA5E'; } // grass 43 | else { color = '#004F2D'; } // forest 44 | 45 | context.fillStyle = `${color}`; 46 | context.fillRect(xPos, yPos, tileSize, tileSize); 47 | }); 48 | 49 | done(); 50 | }); 51 | 52 | return true; 53 | } -------------------------------------------------------------------------------- /visualisations/2d-wood-image.js: -------------------------------------------------------------------------------- 1 | function draw2DWoodImage({ 2 | getValueFn, 3 | offsetX, 4 | offsetY, 5 | width, 6 | height 7 | }) { 8 | height -= 100; 9 | width -= 200; 10 | 11 | context.clearRect(0, 0, screenWidth, screenHeight); 12 | text(offsetX/2 + screenWidth/2, screenHeight/2, 'Calculating...', {color: '#666'}); 13 | 14 | const calculate = () => { 15 | const result = []; 16 | const xPosBase = offsetX + 100; 17 | const yPosBase = offsetY + height + 100; 18 | 19 | let maxValue = -Infinity; 20 | let minValue = Infinity; 21 | 22 | for (let y = 0; y < height; y++) { 23 | for (let x = 0; x < width; x++) { 24 | const value = getValueFn(x, y); 25 | 26 | maxValue = max(maxValue, value); 27 | minValue = min(minValue, value); 28 | 29 | result.push([xPosBase + x, yPosBase - y, value]); 30 | } 31 | } 32 | 33 | result.forEach(r => { 34 | const g = map(minValue, maxValue, 0, 1, r[2]) * 10; 35 | const val = g - floor(g); 36 | r[2] = map(0, 1, 0, 255, val); 37 | }); 38 | 39 | return result; 40 | } 41 | 42 | run(calculate, {getValueFn, height, width, offsetX, offsetY}, result => { 43 | context.clearRect(0, 0, screenWidth, screenHeight); 44 | 45 | result.forEach(([xPos, yPos, value]) => { 46 | context.fillStyle = `rgb(${value}, ${value}, ${value})`; 47 | context.fillRect(xPos, yPos, 1, 1); 48 | }); 49 | }); 50 | } --------------------------------------------------------------------------------