Skip to content

Instantly share code, notes, and snippets.

@mqxu
Created March 14, 2021 16:58
Show Gist options
  • Save mqxu/4d2b8096e2016cdef9389d52eff8bc8c to your computer and use it in GitHub Desktop.
Save mqxu/4d2b8096e2016cdef9389d52eff8bc8c to your computer and use it in GitHub Desktop.
Generative macOS Big Sur Waves 🌊 [SVG]

Generative macOS Big Sur Waves 🌊 [SVG]

This is a generative program that creates macOS Big Sur style wave backgrounds. Each image is random within constraints

The waves generated here may not always look perfect, but they are unique! If you find an output you like, you can copy the SVG to your clipboard using the download button πŸš€

Interested in a tutorial on how I made this? Let me know!

A Pen by George Francis on CodePen.

License.

<div class="canvas-wrapper">
<svg class="canvas" viewBox="0 0 1920 1080" preserveAspectRatio="xMaxYMid slice"></svg>
</div>
<div class="controls">
<p>* Export is disabled for mobile devices</p>
<button class="download">Copy SVG</button>
<button class="regenerate">Regenerate</button>
</div>
console.clear();
import { SVG } from "https://cdn.skypack.dev/@svgdotjs/svg.js";
import {
random,
map,
spline,
pointsInPath
} from "https://cdn.skypack.dev/@georgedoescode/generative-utils@1.0.0";
import tinycolor from "https://cdn.skypack.dev/tinycolor2@1.4.2";
import "https://cdn.skypack.dev/@svgdotjs/svg.filter.js";
import copy from "https://cdn.skypack.dev/copy-to-clipboard@3.3.1";
import { gsap } from "https://cdn.skypack.dev/gsap@3.6.0";
const svg = SVG(".canvas");
const { width, height } = svg.viewbox();
const regenerateBtn = document.querySelector(".regenerate");
const downloadBtn = document.querySelector(".download");
function wave(start, end, gradient) {
const numSteps = random(4, 8, true);
const step = 1 / numSteps;
const randomRange = random(32, 64);
const points = [];
let pointPosition = 0;
for (let i = 0; i <= numSteps; i++) {
const step = map(i, 0, numSteps, 0, 1);
let x = lerp(start.x, end.x, step);
let y = lerp(start.y, end.y, step);
if (i !== 0 && i !== numSteps) {
x += random(-randomRange, randomRange);
y += random(-randomRange, randomRange);
}
points.push({ x, y });
}
const pathData =
spline(points, 1, false) + `L ${end.x} ${height} L ${start.x} ${height} Z`;
const path = svg.path(pathData).attr("fill", gradient);
}
function lerp(v0, v1, t) {
return v0 * (1 - t) + v1 * t;
}
function generate() {
const numWaves = 7;
const base = tinycolor(`hsl(${random(0, 360)}, 65%, 55%)`);
const colors = base.analogous(6);
svg.rect(width, height).fill(random(colors).clone().darken(40).toString());
for (let i = 0; i < numWaves; i++) {
const randomOffset = random(-50, 50);
const originY = map(i, 0, numWaves, -height / 2, height / 3) + randomOffset;
const endY = map(i, 0, numWaves, 0, 1000) + randomOffset;
const color = random(colors).clone();
if (i < 3) {
color.darken(50).desaturate(10);
}
const gradientOffset = map(i, 0, numWaves, 0.1, 1);
let gradient = svg.gradient("linear", function (add) {
add.stop(0, color.clone().lighten(30).toHexString());
add.stop(gradientOffset, color.clone().spin(60).toHexString());
});
gradient.from(0.5, 0).to(0, 1);
wave({ x: 0, y: originY }, { x: width, y: endY }, gradient);
}
}
generate();
regenerateBtn.addEventListener("click", () => {
svg.clear();
generate();
});
downloadBtn.addEventListener("click", () => {
copy(svg.node.outerHTML);
downloadBtn.innerHTML = "Copied to Clipboard!";
setTimeout(() => {
downloadBtn.innerHTML = "Copy SVG";
}, 1500);
});
gsap.fromTo(
".controls",
{
opacity: 0,
y: 24
},
{
opacity: 1,
y: 0,
ease: "back.out(1.7)",
delay: 0.175
}
);
gsap.fromTo(
"button",
{
opacity: 0,
y: 12
},
{
opacity: 1,
y: 0,
stagger: 0.125,
delay: 0.175,
ease: "back.out(1.7)"
}
);
gsap.fromTo(
"p",
{
opacity: 0,
y: 12
},
{
opacity: 1,
y: 0,
ease: "back.out(1.7)",
delay: 0.175
}
);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
padding: 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
color: hsl(0, 0%, 10%);
background: hsl(0, 0%, 100%);
}
.canvas-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.canvas {
width: 100vw;
height: 100vh;
}
.controls {
position: relative;
display: grid;
grid-gap: 1rem;
grid-template-columns: 1fr;
width: 100%;
max-width: 24rem;
padding: 1rem;
background: hsla(0, 0%, 100%, 0.25);
z-index: 1;
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
border-radius: 0.5rem;
border: 1px solid hsla(0, 0%, 100%, 0.25);
opacity: 0;
}
.controls p {
grid-column: -1 / 1;
font-style: italic;
text-align: center;
font-size: 0.75rem;
}
button {
width: 100%;
height: 2.25rem;
border: 0;
font-weight: 500;
font-family: inherit;
border-radius: 0.5rem;
font-size: 0.875rem;
line-height: 2.25rem;
background: hsla(0, 0%, 100%, 0.25);
cursor: pointer;
}
button:nth-of-type(1) {
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
display: none;
}
button:nth-of-type(2) {
background: linear-gradient(to bottom, #287cfe, #0466ff);
color: #fff;
}
@media only screen and (min-width: 640px) {
.controls {
grid-template-columns: 1fr 1fr;
}
.controls p {
display: none;
}
.controls button:nth-of-type(1) {
display: block;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment