<div class="generator">
  <svg class="generator__canvas" viewBox="0 0 200 200">
    <rect x="0" y="0" width="100%" height="100%" fill="#ffffff"></rect>
  </svg>
  <div class="[ stack ] [ generator__controls ]">
    <!-- Pattern -->
    <div class="generator__control generator__control--switch">
      <p class="generator__control-label">Pattern</p>
      <button class="switch-btn" aria-pressed="false" role="switch" aria-labelledby="patternLabel" id="modeSwitch">
        <span class="switch-btn__marker"></span>
        <span class="[ center-xy ] switch-btn__label">Lines</span>
        <span class="[ center-xy ] switch-btn__label">Dots </span>
      </button>
    </div>
    <!-- Grid size -->
    <div class="generator__control generator__control--range">
      <label class="generator__control-label" for="cellSizeSlider">Cell Size</label>
      <input class="range-input" type="range" name="cellSize" id="cellSizeSlider" aria-labelledby="cellSizeLabel" min="0" max="3" step="1" value="0">
    </div>
    <!-- Variance -->
    <div class="generator__control generator__control--range">
      <label class="generator__control-label" for="variance">Variance</label>
      <input class="range-input" type="range" name="variance" id="variance" min="0" max="1" value="0.5" step="0.1">
    </div>
    <!-- Color -->
    <div class="generator__control generator__control--color">
      <label class="generator__control-label" for="color">Color</label>
      <input type="color" name="color" id="color" value="#1a1a1a">
    </div>
    <div class="generator__buttons">
      <button class="generate">Regenerate</button>
      <button class="clipboard">Copy SVG</button>
    </div>
  </div>

</div>
<div class="wave">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" preserveAspectRatio="xMidYMid slice">
    <path fill="#0099ff" fill-opacity="1" d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
  </svg>
  <div></div>
</div>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --gray-dark: hsl(0, 0%, 8%);
  --gray-med: hsl(0, 0%, 88%);
  --gray-light: hsl(0, 0%, 97%);
}

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1rem;
  font-family: "Poppins", sans-serif;
  color: var(--gray-dark);
  line-height: 1;
}

.stack {
  --space: 1.5rem;

  > * + * {
    margin-top: var(--space);
  }
}

.generator {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 28rem;
  background: #fff;
  padding: 1rem;
  border-radius: 1.5rem;
  box-shadow: 0 2.8px 2.2px rgba(0, 0, 0, 0.006),
    0 6.7px 5.3px rgba(0, 0, 0, 0.008), 0 12.5px 10px rgba(0, 0, 0, 0.01),
    0 22.3px 17.9px rgba(0, 0, 0, 0.012), 0 41.8px 33.4px rgba(0, 0, 0, 0.014),
    0 100px 80px rgba(0, 0, 0, 0.02);

  &__canvas {
    margin-bottom: 2rem;
  }

  &__control {
    display: grid;
    grid-template-columns: 112px 1fr;
    align-items: center;
    height: 3rem;

    &-label {
      font-weight: 600;
    }

    &--switch {
      .switch-btn {
        --height: 3rem;

        position: relative;
        display: grid;
        grid-template-columns: 1fr 1fr;
        align-items: center;
        width: 100%;
        height: var(--height);
        margin: 0 auto;
        border-radius: calc(var(--height) / 2);
        border: 0;
        background: var(--gray-light);

        > * {
          pointer-events: none;
        }

        &__label {
          position: relative;
          height: 100%;
        }

        &__marker {
          position: absolute;
          width: calc(50% - 0.25rem);
          height: calc(100% - 0.5rem);
          background: hsl(0, 0%, 100%);
          left: 0.25rem;
          border-radius: calc(var(--height) / 2);
          box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.05);
          transition: transform 200ms ease-in-out;
        }

        &[aria-pressed="true"] {
          .switch-btn__marker {
            transform: translateX(100%);
          }
        }
      }
    }

    &--range {
      .range-input {
        width: 100%;
        cursor: pointer;

        --thumb-size: 1.5rem;
        --track-height: 0.25rem;

        outline-offset: 0.25rem;

        &:focus {
          outline: 2px solid #f0417c;
        }

        &::-webkit-slider-thumb {
          -webkit-appearance: none;
          border: 1px solid var(--gray-med);
          height: var(--thumb-size);
          width: var(--thumb-size);
          border-radius: 50%;
          background: #ffffff;
          margin-top: -10px;
          box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.05);
        }

        &::-moz-range-thumb {
          border: 1px solid var(--gray-med);
          height: var(--thumb-size);
          width: var(--thumb-size);
          border-radius: 50%;
          background: #ffffff;
          margin-top: -10px;
          box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.05);
        }

        &::-webkit-slider-runnable-track {
          width: 100%;
          height: var(--track-height);
          background: var(--gray-light);
          box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, 0.05);
          border-radius: 0.25rem;
          margin: 0.5rem;
        }

        &::-moz-range-track {
          width: 100%;
          height: var(--track-height);
          background: var(--gray-light);
          box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, 0.5);
          border-radius: 0.25rem;
        }
      }
    }

    &--color {
      input {
        width: 100%;
        border: none;
        padding: 0.5rem;
        height: 3rem;
        cursor: pointer;
        overflow: hidden;
        background: var(--gray-light);
        border-radius: 0.5rem;
      }
    }
  }

  &__buttons {
    display: grid;
    grid-template-columns: 1fr;
    grid-gap: 1rem;
    margin-top: 2.2rem;

    button {
      height: 3rem;
      border: 0;
      border-radius: 1.5rem;
      font-weight: 600;
      transition: transform 150ms ease-in-out;
      transform: translate3d(0px, 0px, 0px);

      &:hover {
        transform: scale(1.075);
      }

      &:first-of-type {
        background: #f0417c;
        color: #fff;
      }

      &:nth-of-type(2) {
        background: #fff;
        border: 2px solid var(--gray-dark);
      }
    }
  }

  @media only screen and (min-width: 32rem) {
    padding: 1.5rem;
  }

  @media only screen and (min-width: 62rem) {
    position: relative;
    display: grid;
    grid-template-columns: 440px 1fr;
    max-width: 62rem;
    align-items: center;
    padding-right: 0;

    &__canvas {
      grid-column: 1;
      margin-bottom: 0;
      max-width: 100%;
    }

    &__controls {
      grid-column: 2;
      padding: 0 3.5rem;
    }

    &__buttons {
      grid-column: 2;
      grid-template-columns: 1fr 1fr;
    }
  }
}

button {
  font-family: inherit;
  cursor: pointer;
  font-size: 0.875rem;
}

label {
  display: block;
}

.center-xy {
  display: grid;
  place-items: center;
}

input[type="range"] {
  -webkit-appearance: none;
  width: 100%;
  background: transparent;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
}

input[type="range"]:focus {
  outline: none;
}

input[type="range"]::-ms-track {
  width: 100%;
  cursor: pointer;

  background: transparent;
  border-color: transparent;
  color: transparent;
}

button,
input {
  outline-offset: 0.25rem;

  &:focus {
    outline: 2px solid #f0417c;
  }
}

.wave {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100vw;
  height: 75vh;
  --fill: var(--gray-light);

  svg {
    height: 320px;
    width: 100%;
    transform: translateY(16px);

    path {
      fill: var(--fill);
    }
  }

  div {
    height: 100%;
    width: 100%;
    background: var(--fill);
  }
}
View Compiled
import { SVG } from "https://cdn.skypack.dev/@svgdotjs/svg.js";
import SimplexNoise from "https://cdn.skypack.dev/simplex-noise@2.4.0";
import { map } from "https://cdn.skypack.dev/@georgedoescode/generative-utils";
import debounce from "https://cdn.skypack.dev/debounce@1.2.1";
import copy from "https://cdn.skypack.dev/copy-to-clipboard@3.3.1";

const simplex = new SimplexNoise();

const width = 200;
const height = 200;

let res = 10;

let cols = width / res;
let rows = height / res;

const svg = SVG(".generator__canvas");

let mode = "LINES";
let color = "#1a1a1a";

let noiseInc = 0.05;

function generate() {
  svg.clear();

  let xOff = Math.random() * 1000;

  for (let i = 0; i < cols; i++) {
    let yOff = 0;

    xOff += noiseInc;
    for (let j = 0; j < rows; j++) {
      const noise = simplex.noise2D(xOff, yOff);
      const scale = map(noise, -1, 1, 0.125, 0.75);
      const rotate = map(noise, -1, 1, 0, 360);

      if (mode === "LINES") {
        svg
          .line(i * res, j * res, i * res + res, j * res + res)
          .scale(0.25)
          .stroke({
            color: color,
            width: 4,
            linecap: "round"
          })
          .rotate(rotate);
      } else {
        svg
          .circle(res)
          .x(i * res)
          .y(j * res)

          .fill(color)
          .scale(scale);
      }

      yOff += noiseInc;
    }
  }
}

generate();

document.querySelector(".generate").addEventListener("click", () => {
  generate();
});

document.querySelector("#cellSizeSlider").addEventListener("input", (e) => {
  const val = parseInt(e.target.value);
  const options = [10, 20, 25, 40];

  res = options[val];
  cols = width / res;
  rows = height / res;

  console.log(res);

  generate();
});

document.querySelector("#variance").addEventListener(
  "input",
  debounce((e) => {
    const val = parseFloat(e.target.value);
    noiseInc = map(val, 0, 1, 0.025, 0.075);
    generate();
  }, 10)
);

document.querySelector("#modeSwitch").addEventListener("click", (e) => {
  if (e.target.getAttribute("aria-pressed") === "false") {
    mode = "DOTS";
  } else {
    mode = "LINES";
  }
  generate();
});

document.querySelector("#color").addEventListener(
  "input",
  debounce((e) => {
    color = e.target.value;

    svg.node.querySelectorAll("*").forEach((el) => {
      console.log(el);
      if (mode === "DOTS") {
        el.setAttribute("fill", color);
      } else {
        el.setAttribute("stroke", color);
      }
    });
  }, 10)
);

document.querySelectorAll(".switch-btn").forEach((el) => {
  el.addEventListener("click", (e) => {
    const pressed = e.target.getAttribute("aria-pressed") === "true";
    e.target.setAttribute("aria-pressed", String(!pressed));
  });
});

document.querySelector(".clipboard").addEventListener("click", (e) => {
  copy(svg.node.outerHTML);

  e.target.innerHTML = "Copied to Clipboard!";

  setTimeout(() => {
    e.target.innerHTML = "Copy SVG";
  }, 1500);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.