Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
33b0c5f
saving draft
uuuulala Nov 4, 2025
0cf9762
saving
uuuulala Nov 4, 2025
2917012
draft
uuuulala Nov 9, 2025
777ade9
draft
uuuulala Nov 9, 2025
7ef44c0
Merge remote-tracking branch 'origin/main' into logo-folds
uuuulala Nov 9, 2025
41df7b3
test logos rotation
uuuulala Nov 10, 2025
2272dbb
new params, advanced logic, basic coloring
uuuulala Nov 10, 2025
852955b
fixing build
uuuulala Nov 10, 2025
4db5009
u_angle origin fix
uuuulala Nov 11, 2025
8e9af67
sample logos update
uuuulala Nov 11, 2025
00fbb82
halftone lines template
uuuulala Nov 11, 2025
81c38af
u_colors support
uuuulala Nov 11, 2025
abc8d05
tweaks
uuuulala Nov 11, 2025
7429f5e
alpha-mask original idea
uuuulala Nov 11, 2025
935ac64
back to 512 workingSize
uuuulala Nov 11, 2025
42cea26
saving progress
uuuulala Nov 12, 2025
b48e0ef
saving progress on halftone lines
uuuulala Nov 12, 2025
d046bdc
saving progress on both
uuuulala Nov 13, 2025
5a78358
halftoneLines clean
uuuulala Nov 13, 2025
8f8b7bd
halftoneLines blur limit
uuuulala Nov 15, 2025
e031f8d
antialiased shift
uuuulala Nov 15, 2025
a58c3c8
saving progress on folds
uuuulala Nov 15, 2025
e19fa8d
new logo-spots sketch
uuuulala Nov 15, 2025
059e3e2
halftone lines cleanup + origColor support
uuuulala Nov 16, 2025
6081d42
folds fix and clean up
uuuulala Nov 16, 2025
1299796
logo list update
uuuulala Nov 17, 2025
6faf74a
folds adjusted
uuuulala Nov 17, 2025
e7b4fc3
blobs dynamic colors + cleanup
uuuulala Nov 17, 2025
ceb3e5f
folds polishing
uuuulala Nov 17, 2025
93a95b7
halftone presets
uuuulala Nov 17, 2025
de4f70f
blobsLogo renaming
uuuulala Nov 17, 2025
69b9b0b
halftoneLines classic preset
uuuulala Nov 17, 2025
4b58c9d
2 more folds presets
uuuulala Nov 18, 2025
3f458c9
warp sketch to save
uuuulala Nov 19, 2025
42ee0e1
progress
uuuulala Nov 24, 2025
99a2555
progress
uuuulala Nov 24, 2025
1101618
progress
uuuulala Nov 24, 2025
75ccfbb
clean-up
uuuulala Nov 24, 2025
543d1be
polishing
uuuulala Nov 24, 2025
c8c1b87
logos update
uuuulala Nov 24, 2025
78e776d
new preset
uuuulala Nov 24, 2025
4ba32cd
text replacements
uuuulala Nov 24, 2025
d013141
allowing more distortion
uuuulala Nov 24, 2025
6d26168
experimenting with 3d
uuuulala Nov 25, 2025
be3c3c0
blobs to voronoi
uuuulala Nov 25, 2025
1db44a9
blobs to fbm
uuuulala Nov 25, 2025
949db84
more experiments on 3d look
uuuulala Nov 25, 2025
0dfac04
GemSmoke component renamed + defs + controls reordering
uuuulala Nov 25, 2025
9c8e9d1
Merge branch 'main' into logo-folds
uuuulala Nov 25, 2025
6d1e6a7
gemSmoke defs update
uuuulala Nov 27, 2025
ebc396d
progress on 3d
uuuulala Nov 28, 2025
75e8b82
3d progress
uuuulala Nov 28, 2025
ca62a0a
3d progress
uuuulala Nov 28, 2025
1f79c24
Merge branch 'main' into logo-folds
uuuulala Nov 28, 2025
73a1a7b
grains on HTD
uuuulala Nov 28, 2025
14a18bc
adj
uuuulala Nov 28, 2025
57b6228
start on grid types
uuuulala Nov 29, 2025
606ddf3
new line params, polishing
uuuulala Nov 30, 2025
1190fff
consistent noise/angle distortion
uuuulala Nov 30, 2025
d49d0e9
progress
uuuulala Nov 30, 2025
978c99d
splitting grainSize
uuuulala Dec 1, 2025
3a0c33a
clean-up
uuuulala Dec 1, 2025
91ccdcb
presets
uuuulala Dec 1, 2025
494525c
leaving only the halftone lines
uuuulala Dec 2, 2025
cc938ef
added u_gridAngleDistortion back to radial grid
uuuulala Dec 2, 2025
ecba100
shaders defs
uuuulala Dec 2, 2025
0941a18
shader description added
uuuulala Dec 2, 2025
35e92c2
smoothness blur fixed ranges
uuuulala Dec 9, 2025
6ee6f74
transparency support + GLSL clean-up
uuuulala Dec 9, 2025
7f6834d
Merge remote-tracking branch 'origin/main' into halftone-lines
uuuulala Dec 9, 2025
c604564
sync test logo set with main
uuuulala Dec 9, 2025
336c72f
registry example added, formatting
uuuulala Dec 9, 2025
819786e
arty preset
uuuulala Dec 11, 2025
67e3db4
Merge remote-tracking branch 'origin/main' into halftone-lines
uuuulala Dec 17, 2025
52866db
replacing getImageUV with vertex uv
uuuulala Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/registry/halftone-lines-example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use client';

import { HalftoneLines, HalftoneLinesProps } from '@paper-design/shaders-react';

export function HalftoneLinesExample(props: HalftoneLinesProps) {
return <HalftoneLines style={{ position: 'fixed', width: '100%', height: '100%' }} {...props} />;
}
9 changes: 9 additions & 0 deletions docs/src/app/(shaders)/halftone-lines/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Halftone Lines Filter • Paper',
};

export default function Layout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
133 changes: 133 additions & 0 deletions docs/src/app/(shaders)/halftone-lines/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use client';

import { HalftoneLines, halftoneLinesPresets } from '@paper-design/shaders-react';
import { useControls, button, folder } from 'leva';
import { setParamsSafe, useResetLevaParams } from '@/helpers/use-reset-leva-params';
import { usePresetHighlight } from '@/helpers/use-preset-highlight';
import { cleanUpLevaParams } from '@/helpers/clean-up-leva-params';
import { HalftoneLinesGrid, HalftoneLinesGrids, halftoneLinesMeta, ShaderFit } from '@paper-design/shaders';
import { levaImageButton } from '@/helpers/leva-image-button';
import { useState, useEffect, useCallback } from 'react';
import { toHsla } from '@/helpers/color-utils';
import { ShaderDetails } from '@/components/shader-details';
import { halftoneLinesDef } from '@/shader-defs/halftone-lines-def';
import { ShaderContainer } from '@/components/shader-container';
import { useUrlParams } from '@/helpers/use-url-params';

const { worldWidth, worldHeight, ...defaults } = halftoneLinesPresets[0].params;

const imageFiles = [
'001.webp',
'002.webp',
'003.webp',
'004.webp',
'005.webp',
'006.webp',
'007.webp',
'008.webp',
'009.webp',
'0010.webp',
'0011.webp',
'0012.webp',
'0013.webp',
'0014.webp',
'0015.webp',
'0016.webp',
'0017.webp',
'0018.webp',
] as const;

const HalftoneLinesWithControls = () => {
const [imageIdx, setImageIdx] = useState(-1);
const [image, setImage] = useState<HTMLImageElement | string>('/images/image-filters/0018.webp');
const [status, setStatus] = useState('Click to load an image');

useEffect(() => {
if (imageIdx >= 0) {
const name = imageFiles[imageIdx];
setStatus(`Displaying image: ${name}`);
const img = new Image();
img.src = `/images/image-filters/${name}`;
img.onload = () => setImage(img);
}
}, [imageIdx]);

const handleClick = useCallback(() => {
setImageIdx((prev) => (prev + 1) % imageFiles.length);
}, []);

const setImageWithoutStatus = useCallback((img?: HTMLImageElement) => {
setImage(img ?? '');
setImageIdx(-1);
setStatus(``);
}, []);

const [params, setParams] = useControls(() => {
const presets = Object.fromEntries(
halftoneLinesPresets.map(({ name, params: { worldWidth, worldHeight, ...preset } }) => [
name,
button(() => setParamsSafe(params, setParams, preset)),
])
);
return {
colorBack: { value: toHsla(defaults.colorBack), order: 100 },
colorFront: { value: toHsla(defaults.colorFront), order: 101 },
originalColors: { value: defaults.originalColors, order: 102 },

contrast: { value: defaults.contrast, min: 0.01, max: 1, order: 200 },
inverted: { value: defaults.inverted, order: 201 },
smoothness: { value: defaults.smoothness, min: 0, max: halftoneLinesMeta.maxBlurRadius, order: 202 },
stripeWidth: { value: defaults.stripeWidth, min: 0, max: 1, order: 203 },
thinLines: { value: defaults.thinLines, order: 204 },
allowOverflow: { value: defaults.allowOverflow, order: 205 },

grid: {
value: defaults.grid,
options: Object.keys(HalftoneLinesGrids) as HalftoneLinesGrid[],
order: 250,
},
size: { value: defaults.size, min: 0, max: 1, order: 251 },
gridOffsetX: { value: defaults.gridOffsetX, min: -1, max: 1, order: 252 },
gridOffsetY: { value: defaults.gridOffsetY, min: -1, max: 1, order: 253 },
gridRotation: { value: defaults.gridRotation, min: 0, max: 360, order: 254 },
gridAngleDistortion: { value: defaults.gridAngleDistortion, min: 0, max: 1, order: 255 },
gridNoiseDistortion: { value: defaults.gridNoiseDistortion, min: 0, max: 1, order: 256 },

grainMixer: { value: defaults.grainMixer, min: 0, max: 1, order: 350 },
grainMixerSize: { value: defaults.grainMixerSize, min: 0, max: 1, order: 351 },
grainOverlay: { value: defaults.grainOverlay, min: 0, max: 1, order: 352 },
grainOverlaySize: { value: defaults.grainOverlaySize, min: 0, max: 1, order: 353 },

scale: { value: defaults.scale, min: 0.1, max: 10, order: 400 },
fit: { value: defaults.fit, options: ['contain', 'cover'] as ShaderFit[], order: 450 },
Image: folder(
{
'Upload image': levaImageButton(setImageWithoutStatus),
},
{ order: 0 }
),
Presets: folder(presets, { order: -1 }),
};
});

// Reset to defaults on mount, so that Leva doesn't show values from other
// shaders when navigating (if two shaders have a color1 param for example)
useResetLevaParams(params, setParams, defaults);
useUrlParams(params, setParams, halftoneLinesDef);
usePresetHighlight(halftoneLinesPresets, params);
cleanUpLevaParams(params);

return (
<>
<ShaderContainer shaderDef={halftoneLinesDef} currentParams={params}>
<HalftoneLines onClick={handleClick} {...params} image={image} />
</ShaderContainer>
<div onClick={handleClick} className="mx-auto mt-16 mb-48 w-fit text-base text-current/70 select-none">
Click to change the sample image
</div>
<ShaderDetails shaderDef={halftoneLinesDef} currentParams={params} />
</>
);
};

export default HalftoneLinesWithControls;
173 changes: 173 additions & 0 deletions docs/src/shader-defs/halftone-lines-def.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { halftoneLinesPresets } from '@paper-design/shaders-react';
import type { ShaderDef } from './shader-def-types';
import { staticImageCommonParams } from './common-param-def';

const defaultParams = halftoneLinesPresets[0].params;

export const halftoneLinesDef: ShaderDef = {
name: 'Halftone lines',
description:
'Image filter pattern made of a line grid with optional stroke thickness and grid distortion; offering multiple grid types, grid distortions, and color palettes',
params: [
{
name: 'image',
type: 'HTMLImageElement | string',
description: 'The image to use for the effect',
},
{
name: 'colorBack',
type: 'string',
defaultValue: defaultParams.colorBack,
isColor: true,
description: 'Background color',
},
{
name: 'colorFront',
type: 'string',
defaultValue: defaultParams.colorFront,
isColor: true,
description: 'Foreground color',
},
{
name: 'originalColors',
type: 'boolean',
defaultValue: defaultParams.originalColors,
description: 'Use the sampled image’s original colors instead of colorFront',
options: ['true', 'false'],
},
{
name: 'contrast',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.contrast,
description: 'Contrast applied to the sampled image',
},
{
name: 'inverted',
type: 'boolean',
defaultValue: defaultParams.inverted,
description: 'Inverts the image luminance',
options: ['true', 'false'],
},
{
name: 'smoothness',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.smoothness,
description: 'The size of blur applied to original image, the more image is blurred the smoother is stroke shape',
},
{
name: 'stripeWidth',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.stripeWidth,
description: 'The max width of halftone stroke shape, relative to grid size',
},
{
name: 'thinLines',
type: 'boolean',
defaultValue: defaultParams.thinLines,
description: 'Allows thin 1px strokes (set to false for proper antialiasing)',
options: ['true', 'false'],
},
{
name: 'allowOverflow',
type: 'boolean',
defaultValue: defaultParams.allowOverflow,
description:
'Allows thin stripeWidth to take the whole grid size (set to false to make grid lines always visible)',
options: ['true', 'false'],
},
{
name: 'grid',
type: 'enum',
defaultValue: defaultParams.grid,
description: 'Grid type',
options: ['lines', 'radial', 'waves', 'noise'],
},
{
name: 'size',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.size,
description: 'Grid size relative to the image box',
},
{
name: 'gridOffsetX',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.gridOffsetX,
description: 'The horizontal offset of grid relative to the image box',
},
{
name: 'gridOffsetY',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.gridOffsetY,
description: 'The vertical offset of grid relative to the image box',
},
{
name: 'gridRotation',
type: 'number',
min: 0,
max: 360,
defaultValue: defaultParams.gridRotation,
description: 'The grid rotation around image center (not efficient for radial grid + zero grid offsets)',
},
{
name: 'gridAngleDistortion',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.gridAngleDistortion,
description: 'Changing the grid rotation on the darker image areas',
},
{
name: 'gridNoiseDistortion',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.gridNoiseDistortion,
description: 'Applying noise to the grid on the darker image areas',
},
{
name: 'grainMixer',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.grainMixer,
description: 'Strength of grain distortion applied to strokes',
},
{
name: 'grainMixerSize',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.grainMixerSize,
description: 'The scale applied to the grain distortion',
},
{
name: 'grainOverlay',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.grainOverlay,
description: 'Post-processing b/w grainy overlay',
},
{
name: 'grainOverlaySize',
type: 'number',
min: 0,
max: 1,
defaultValue: defaultParams.grainOverlaySize,
description: 'The scale applied to the grain overlay',
},
...staticImageCommonParams,
],
};
4 changes: 4 additions & 0 deletions packages/shaders-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export { HalftoneDots, halftoneDotsPresets } from './shaders/halftone-dots.js';
export type { HalftoneDotsProps } from './shaders/halftone-dots.js';
export type { HalftoneDotsUniforms, HalftoneDotsParams } from '@paper-design/shaders';

export { HalftoneLines, halftoneLinesPresets } from './shaders/halftone-lines.js';
export type { HalftoneLinesProps } from './shaders/halftone-lines.js';
export type { HalftoneLinesUniforms, HalftoneLinesParams } from '@paper-design/shaders';

export { isPaperShaderElement, getShaderColorFromString } from '@paper-design/shaders';
export type { PaperShaderElement, ShaderFit, ShaderSizingParams, ShaderSizingUniforms } from '@paper-design/shaders';

Expand Down
Loading