<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200"
height="200"
viewBox="0 0 100 100"
role="img"
aria-labelledby="title"
>
<title id="title">
Shining Some Light
</title>
<defs>
<filter id="my-filter">
<feTurbulence baseFrequency="0.1" seed="0" numOctaves="1" />
<feDiffuseLighting lighting-color="DodgerBlue" surfaceScale="10">
<feDistantLight azimuth="45" elevation="60"></feDistantLight>
</feDiffuseLighting>
<feComposite operator="in" in2="SourceGraphic"></feComposite>
</filter>
</defs>
<circle cx=
"50" cy="50" r="50" filter="url('#my-filter')" />
</svg>
<form>
<div class="form-chunk" id="turbulence-settings">
<h2>Turbulence</h2>
<fieldset>
<legend>Base Frequency</legend>
<div>
<label for="baseFrequencyX">x</label>
<input type="range" name="baseFrequencyX" id="baseFrequencyX" value="0.1" min="0.01" max="2" step="0.01">
<span aria-hidden="true" id="baseFrequencyXDisplay">0.01</span>
</div>
<div>
<label for="baseFrequencyY">y</label>
<input type="range" name="baseFrequencyY" id="baseFrequencyY" value="0.1" min="0.01" max="2" step="0.01">
<span aria-hidden="true" id="baseFrequencyYDisplay">0.01</span>
</div>
</fieldset>
<fieldset>
<legend>Type</legend>
<div>
<input type="radio" name="type" id="turbulence" value="turbulence" checked>
<label for="turbulence">Turbulence</label>
</div>
<div>
<input type="radio" name="type" id="fractalNoise" value="fractalNoise">
<label for="fractalNoise">Fractal Noise</label>
</div>
</fieldset>
<div class="fake-fieldset">
<label class="fake-legend" for="seed">Seed</label>
<input type="number" name="seed" id="seed" value="0" />
</div>
<div class="fake-fieldset">
<label class="fake-legend" for="numOctaves">Octaves</label>
<input type="number" name="numOctaves" id="numOctaves" value="1" />
</div>
</div>
<div class="form-chunk" id="lighting-settings">
<h2>Diffuse Lighting</h2>
<div class="fake-fieldset">
<label class="fake-legend" for="color">Color</label>
<input type="color" name="lighting-color" id="color" value="#1e90ff" data-target="feDiffuseLighting"/>
</div>
<div class="fake-fieldset">
<label class="fake-legend" for="surfaceScale">Surface Scale</label>
<input type="number" name="surfaceScale" id="surfaceScale" value="10" data-target="feDiffuseLighting"/>
</div>
<h2>Distant Light</h2>
<div class="fake-fieldset">
<label class="fake-legend" for="elevation">Elevation</label>
<input type="range" name="elevation" id="elevation" value="60" data-target="feDistantLight" min="0" max="360" step="1" />
<span aria-hidden="true" id="elevationDisplay">60</span>
</div>
<div class="fake-fieldset">
<label class="fake-legend" for="azimuth">Azimuth</label>
<input type="range" name="azimuth" id="azimuth" value="45" min="0" max="360" step="1" data-target="feDistantLight"/>
<span aria-hidden="true" id="azimuthDisplay">45</span>
</div>
</div>
</form>
body, html {
align-items: center;
justify-content: center;
margin: 0;
display: flex;
min-height: 100%;
width: 100%;
}
* {
font-family: sans-serif;
box-sizing: border-box;
}
fieldset > div + div {
margin-top: 0.25em;
}
fieldset > div {
display: flex;
align-items: center;
}
* + fieldset,
* + .fake-fieldset {
margin-top: 1em;
}
fieldset, .fake-fieldset {
border: 1px solid #a0a0a0;
padding: 0.5em 1em;
position: relative;
}
.fake-fieldset {
padding-top: 1em;
display: flex;
align-items: center;
}
.fake-legend {
position: absolute;
left: 1em;
top: -0.75em;
font-weight: bold;
background: #fff;
padding: 0.25em;
}
input {
margin-top: 0;
}
[type="range"] {
margin: 0 0.5em;
flex-grow: 1;
width: calc(100% - 2em);
}
[type="number"],
[type="color"]{
display: block;
width: 100%;
box-sizing: border-box;
}
legend {
font-weight: bold;
}
form {
display: flex;
flex-wrap: wrap;
max-width: 100%;
overflow: hidden;
}
.form-chunk {
margin-left: 1em;
flex-grow: 1;
flex-basis: 10em;
flex-shrink: 1;
}
h2 {
font-size: 1.5em;
}
svg {
min-width: 8em;
}
const filter = document.querySelector('filter');
const turbulence = document.querySelector('feTurbulence');
let frequencyX = 0.1;
let frequencyY = 0.1;
document.querySelectorAll('#turbulence-settings input').forEach(input => {
input.addEventListener('input', ({target}) => {
const {name, value} = target;
if(name === 'baseFrequencyX') {
frequencyX = value;
turbulence.setAttribute('baseFrequency', `${frequencyX} ${frequencyY}`)
document.querySelector('#baseFrequencyXDisplay').textContent = frequencyX;
} else if(name === 'baseFrequencyY') {
frequencyY = value;
turbulence.setAttribute('baseFrequency', `${frequencyX} ${frequencyY}`)
document.querySelector('#baseFrequencyYDisplay').textContent = frequencyY;
} else {
turbulence.setAttribute(name, value);
}
});
});
document.querySelectorAll('#lighting-settings input').forEach(input => {
input.addEventListener('input', ({target}) => {
const {name, value, dataset} = target;
document.querySelector(dataset.target).setAttribute(name, value);
if(name === 'azimuth') {
document.querySelector('#azimuthDisplay').textContent = value;
} else if(name === 'elevation') {
document.querySelector('#elevationDisplay').textContent = value;
}
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.