Halftone Dots

open Paper
Monstera leavesPalm fronds on yellowMint sprigsOrange cosmos flowers
  • Tropical
  • Palms
  • Herbs
  • Blooms
back to image demo
Presets

Installation

npm i @paper-design/shaders-react

Code

import { HalftoneDots } from '@paper-design/shaders-react';
import './demo.css';

<HalftoneDots
  colorBack="#fff6d6"
  radius={1.15}
  grainMixer={0}
>
  <div
    className="demo"
    onPointerOver={(event) => {
      const link = event.target.closest('a');
      if (!link) return;
      const links = [...event.currentTarget.querySelectorAll('a')];
      const index = links.indexOf(link);
      links.forEach((other) => other.classList.toggle('active', other === link));
      event.currentTarget.querySelectorAll('img').forEach((image, i) => image.classList.toggle('active', i === index));
    }}
  >
    <img src="/images/image-filters/004.webp" alt="Monstera leaves" className="active" />
    <img src="/images/image-filters/006.webp" alt="Palm fronds on yellow" />
    <img src="/images/image-filters/0015.webp" alt="Mint sprigs" />
    <img src="/images/image-filters/0018.webp" alt="Orange cosmos flowers" />
    <ul>
      <li>
        <a className="active">Tropical</a>
      </li>
      <li>
        <a>Palms</a>
      </li>
      <li>
        <a>Herbs</a>
      </li>
      <li>
        <a>Blooms</a>
      </li>
    </ul>
  </div>
</HalftoneDots>

demo.css

@property --highlight-stroke { syntax: '<length>'; inherits: false; initial-value: 0px }
.demo { --ease: cubic-bezier(0.22, 1, 0.36, 1); position: relative; width: 100%; height: 100%; container-type: inline-size; overflow: hidden; color: #222; background: #fff }
.demo ul { display: grid; align-content: center; justify-items: start; width: 50%; height: 100%; margin: 0; padding: 0 0 0 3cqw; box-sizing: border-box; list-style: none }
.demo a { display: flex; align-items: baseline; gap: 2cqw; padding: 0.8cqw 0; font: 300 80px/1.1 system-ui; letter-spacing: 0.03em; text-transform: lowercase; -webkit-text-stroke: var(--highlight-stroke) currentColor; cursor: default; transition: --highlight-stroke 1000ms var(--ease) }
.demo a.active { --highlight-stroke: 0.065em }
.demo a::after { content: ''; flex: none; width: 0.32em; height: 0.32em; translate: 0 calc(0.16em - 0.5ex); border-radius: 50%; background: currentColor; scale: 0; transition: scale 1000ms var(--ease) }
.demo a.active::after { scale: 1 }
.demo img { position: absolute; top: 0; right: 0; width: 50%; height: 100%; object-fit: cover; opacity: 0; pointer-events: none; transition: opacity 1000ms var(--ease) }
.demo img.active { opacity: 1 }

Shader Props

NameDescriptionTypeValues
imageThe image to use for the effectHTMLImageElement | stringImage object or URL
colorBackBackground colorstringHex, RGB, or HSL color
colorFrontForeground color (needs originalColors off)stringHex, RGB, or HSL color
originalColorsUse the sampled image’s original colors instead of colorFrontboolean
| true | false
typeDot styleenum
| "classic" | "gooey" | "holes" | "soft"
invertedInverts the image luminance, doesn’t affect the color scheme (needs contrast > 0)boolean
| true | false
gridDots grid typeenum
| "square" | "hex"
sizeGrid size relative to the image boxnumber0 to 1
radiusMaximum dot size (relative to the grid cell)number0 to 2
contrastContrast applied to the sampled image (at 0 the luminance is flat, so all the dots get equal size and the image content is lost; with originalColors on the dots still take their color from the image)number0 to 1
grainMixerStrength of grain distortion applied to shape edgesnumber0 to 1
grainOverlayPost-processing b/w grain overlaynumber0 to 1
grainSizeThe scale applied to both grain distortion and grain overlay (needs grainMixer or grainOverlay > 0)number0 to 1

Common Props

NameDescriptionTypeValues
scaleOverall zoom level of the graphicsnumber0.1 to 4
rotationOverall rotation angle of the graphicsnumber0 to 360
offsetXHorizontal offset of the graphics centernumber-1 to 1
offsetYVertical offset of the graphics centernumber-1 to 1
widthCSS width style of the shader elementnumber | string—
heightCSS height style of the shader elementnumber | string—
fitHow to fit the rendered shader into the canvas dimensionsenum
| "contain" | "cover"
originXReference point for positioning world width in the canvasnumber0 to 1
originYReference point for positioning world height in the canvasnumber0 to 1
minPixelRatioMinimum pixel ratio to use when rendering the shader (default is 2 for double resolution)number—
maxPixelCountMaximum pixel count that the shader may processnumber—

Description

A halftone-dot image filter featuring customizable grids, color palettes, and dot styles