<div id="ccc"></div>

<div id="config">
  <div class="config-wrap">
    <label
      id="line-width-label"
      for="line-width"
    >
      Line width (1)
    </label>
    <input
      type="range"
      min="1"
      max="20"
      value="1"
      id="line-width"
      name="line-width"
    />
  </div>
  <div class="config-wrap">
    <label
      id="sides-count-label"
      for="sides-count"
    >
      Sides Count (8)
    </label>
    <input
      type="range"
      min="2"
      max="24"
      step="1"
      value="8"
      id="sides-count"
      name="sides-count"
    />
  </div>
  <div class="config-wrap">
    <label
      id="scale-label"
      for="scale"
    >
      Scale (50)
    </label>
    <input
      type="range"
      min="1"
      max="150"
      step="1"
      value="75"
      id="scale"
      name="scale"
    />
  </div>
  <div class="config-wrap">
    <label
      id="rotation-label"
      for="rotation"
    >
      Rotation (0)
    </label>
    <input
      type="range"
      min="0"
      max="360"
      step="1"
      value="0"
      id="rotation"
      name="rotation"
    />
  </div>
  <div class="config-wrap">
    <label
      id="stroke-color-label"
      for="stroke-color"
    >
      Stroke Color (#2980b9)
    </label>
    <input
      type="color"
      min="1"
      max="20"
      step="1"
      id="stroke-color"
      name="stroke-color"
      value="#2980b9"
    />
  </div>
</div>

<script id="c" type="text/worklet">
  const PAINTLET_NAME = 'loop'

  class CSSPaintlet {
    static get inputProperties() {
      return [
        `--${PAINTLET_NAME}-line-width`,
        `--${PAINTLET_NAME}-stroke-color`,
        `--${PAINTLET_NAME}-sides`,
        `--${PAINTLET_NAME}-scale`,
        `--${PAINTLET_NAME}-rotation`,
      ]
    }

    paint(ctx, paintSize, props, args) {
      const lineWidth = Number(props.get(`--${PAINTLET_NAME}-line-width`))
      const strokeColor = props.get(`--${PAINTLET_NAME}-stroke-color`)
      const numSides = Number(props.get(`--${PAINTLET_NAME}-sides`))
      const scale = Number(props.get(`--${PAINTLET_NAME}-scale`))
      const rotation = Number(props.get(`--${PAINTLET_NAME}-rotation`))
      
      const angle = Math.PI * 2 / numSides
      const radius = paintSize.height / 2
      ctx.save()
      ctx.lineWidth = lineWidth
      ctx.lineJoin = 'round'
      ctx.lineCap = 'round'
      ctx.strokeStyle = strokeColor
      ctx.translate(paintSize.width / 2, paintSize.height / 2)
      ctx.rotate(rotation * (Math.PI / 180))
      ctx.scale(scale / 100, scale / 100)
      ctx.moveTo(0, radius)
      for (let i = 0; i < numSides; i++) {
        const x = Math.sin(i * angle) * radius
        const y = Math.cos(i * angle) * radius
        for (let n = i; n < numSides; n++) {
          const x2 = Math.sin(n * angle) * radius
          const y2 = Math.cos(n * angle) * radius
          ctx.lineTo(x, y)
          ctx.lineTo(x2, y2);
        }
      }
      ctx.closePath()
      ctx.stroke()
      ctx.restore()
    }   
  }

  registerPaint(PAINTLET_NAME, CSSPaintlet)
</script>
* {
  margin: 0;
  padding: 0;
  font-family: Helvetica, sans-serif;
}

#ccc {
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  
  --loop-line-width: 1;
  --loop-stroke-color: #2980b9;
  --loop-sides: 8;
  --loop-scale: 100;
  --loop-rotation: 0;

  background-image: paint(loop);
  background-repeat: no-repeat;
  background-size: cover;
 
}

#config {
  position: fixed;
  bottom: 0;
  left: 0;
  padding: 1rem;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(3px);
  box-sizing: border-box;
  border-top-right-radius: 24px;
}

.config-wrap {
  margin-bottom: 0.75rem;
  color: rgba(0, 0, 0, 0.8);
}

label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}
console.clear()

const paintletCode = document.getElementById('c')

;(async function() {
  // ⚠️ Handle Firefox and Safari by importing a polyfill for CSS Pain    
  if (CSS['paintWorklet'] === undefined) {
    await import('https://unpkg.com/css-paint-polyfill')
  }

  // Explicitly define our custom CSS variable
  // Make sure that the browser treats it as a number
  
  if ('registerProperty' in CSS) {
    CSS.registerProperty({
      name: '--loop-line-width',
      syntax: '<number>',
      inherits: false,
      initialValue: 1
    })
    CSS.registerProperty({
      name: '--loop-stroke-color',
      syntax: '<color>',
      inherits: false,
      initialValue: '#2980b9'
    })
    CSS.registerProperty({
      name: '--loop-sides',
      syntax: '<number>',
      inherits: false,
      initialValue: 8
    })
    CSS.registerProperty({
      name: '--loop-scale',
      syntax: '<number>',
      inherits: false,
      initialValue: 100
    })
    CSS.registerProperty({
      name: '--loop-rotation',
      syntax: '<number>',
      inherits: false,
      initialValue: 0
    })
  }
  
  
  
  const lineWidthInput = document.getElementById('line-width')
  const sidesCountInput = document.getElementById('sides-count')
  const scaleInput = document.getElementById('scale')
  const rotationInput = document.getElementById('rotation')
  const strokeInput = document.getElementById('stroke-color')
  
  const lineWidthLabel = document.getElementById('line-width-label')
  const sidesCountLabel = document.getElementById('sides-count-label')
  const scaleLabel = document.getElementById('scale-label')
  const rotationLabel = document.getElementById('rotation-label')
  const strokeLabel = document.getElementById('color-color-label')
  
  const workletElement = document.getElementById('ccc')
  
  lineWidthInput.addEventListener('change', e => {
    const lineWidth = Number(e.target.value)
    lineWidthLabel.textContent = `Line width (${lineWidth})`
    workletElement.style.setProperty('--loop-line-width', lineWidth)
  })
  sidesCountInput.addEventListener('change', e => {
    const sidesCount = Number(e.target.value)
    sidesCountLabel.textContent = `Sides Count (${sidesCount})`
    workletElement.style.setProperty('--loop-sides', sidesCount)
  })
  scaleInput.addEventListener('change', e => {
    const scale = Number(e.target.value)
    scaleLabel.textContent = `Scale (${scale})`
    workletElement.style.setProperty('--loop-scale', scale)
  })
  rotationInput.addEventListener('change', e => {
    const rotation = Number(e.target.value)
    rotationLabel.textContent = `Rotation (${rotation})`
    workletElement.style.setProperty('--loop-rotation', rotation)
  })
  strokeInput.addEventListener('change', e => {
    const strokeColor = e.target.value
    rotationLabel.textContent = `Stroke Color (${strokeColor})`
    workletElement.style.setProperty('--loop-stroke-color', strokeColor)
  })

  // Include our paintlet using
  registerCSSPaintlet(paintletCode.textContent)
})()

function registerCSSPaintlet (sourceCode) {  CSS.paintWorklet.addModule(`data:application/javascript;charset=utf8,${encodeURIComponent(sourceCode)}`)
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.