Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 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
d8a572f
keeping only Folds
uuuulala Dec 2, 2025
fa38cfe
Folds => Logo3d renaming
uuuulala Dec 2, 2025
d2fac16
clean-up + 3d oberlay shadow AA
uuuulala Dec 5, 2025
85d0ab5
saving progress
uuuulala Dec 6, 2025
37124fe
saving progress
uuuulala Dec 6, 2025
2235931
testing bumped version build
uuuulala Dec 6, 2025
ada722f
saving progress
uuuulala Dec 6, 2025
4e7c28f
sync with main
uuuulala Dec 7, 2025
fa5b911
test logo set sync with main
uuuulala Dec 7, 2025
e3c9819
overlayHeight + presets improvements
uuuulala Dec 7, 2025
d6e931d
defaults tweak
uuuulala Dec 9, 2025
81f4e2d
max 6 lights
uuuulala Dec 9, 2025
4f97ec9
Merge remote-tracking branch 'origin/main' into 3d-logo
uuuulala Feb 3, 2026
75d91b0
post-merge
uuuulala Feb 3, 2026
ca14828
remove underlay, hardcode some params
uuuulala Feb 4, 2026
be8ce3e
optimisation
uuuulala Feb 4, 2026
0c0c144
underlay color removed
uuuulala Feb 4, 2026
6df37de
renamings, presets
uuuulala Feb 4, 2026
1d5925b
back to glsl blur
uuuulala Feb 4, 2026
15fb2be
animation back; start reworking the overlay shape
uuuulala Feb 4, 2026
471a91d
configurable shadow
uuuulala Feb 4, 2026
1efb569
remove overlay
uuuulala Feb 4, 2026
868bc15
clean-up, no overlay
uuuulala Feb 7, 2026
b2c143e
simplify
uuuulala Feb 7, 2026
cbc9068
iterating
uuuulala Feb 8, 2026
bc398c2
iterating
uuuulala Feb 9, 2026
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
9 changes: 9 additions & 0 deletions docs/src/app/(shaders)/logo-3d/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: '3d Logo Filter • Paper',
};

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

import { Logo3d, logo3dPresets } 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 { logo3dMeta } from '@paper-design/shaders';
import { ShaderFit } from '@paper-design/shaders';
import { levaImageButton } from '@/helpers/leva-image-button';
import { useState, Suspense, useEffect, useCallback } from 'react';
import { ShaderDetails } from '@/components/shader-details';
import { ShaderContainer } from '@/components/shader-container';
import { useUrlParams } from '@/helpers/use-url-params';
import { logo3dDef } from '@/shader-defs/logo-3d-def';
import { toHsla } from '@/helpers/color-utils';
import { useColors } from '@/helpers/use-colors';

// Override just for the docs, we keep it transparent in the preset
// logo3dPresets[0].params.colorBack = '#000000';

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

const imageFiles = [
'contra.svg',
'apple.svg',
// 'paradigm.svg',
'paper-logo-only.svg',
// 'brave.svg',
// 'capy.svg',
// 'infinite.svg',
'linear.svg',
'mercury.svg',
'mymind.svg',
'resend.svg',
'shopify.svg',
'wealth-simple.svg',
'chanel.svg',
'cibc.svg',
'cloudflare.svg',
'discord.svg',
'nasa.svg',
'nike.svg',
'volkswagen.svg',
'diamond.svg',
] as const;

const Logo3dWithControls = () => {
const [imageIdx, setImageIdx] = useState(-1);
const [image, setImage] = useState<HTMLImageElement | string>('/images/logos/diamond.svg');

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

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

const { colors, setColors } = useColors({
defaultColors: defaults.colors,
maxColorCount: logo3dMeta.maxColorCount,
});

const [params, setParams] = useControls(() => {
return {
colorBack: { value: toHsla(defaults.colorBack), order: 100 },
colorBase: { value: toHsla(defaults.colorBase), order: 102 },
lightsSpread: { value: defaults.lightsSpread, min: 0, max: 1, order: 207 },
lightsDiffuse: { value: defaults.lightsDiffuse, min: 0, max: 1, order: 209 },
lightsSpecular: { value: defaults.lightsSpecular, min: 0, max: 1, order: 210 },
lightsShadow: { value: defaults.lightsShadow, min: 0, max: 1, order: 211 },
speed: { value: defaults.speed, min: 0, max: 2, order: 300 },
scale: { value: defaults.scale, min: 0.2, max: 10, order: 301 },
// rotation: { value: defaults.rotation, min: 0, max: 360, order: 302 },
// offsetX: { value: defaults.offsetX, min: -1, max: 1, order: 303 },
// offsetY: { value: defaults.offsetY, min: -1, max: 1, order: 304 },
// fit: { value: defaults.fit, options: ['contain', 'cover'] as ShaderFit[], order: 305 },
Image: folder(
{
'Upload image': levaImageButton((img?: HTMLImageElement) => setImage(img ?? '')),
},
{ order: -1 }
),
};
}, [colors.length]);

useControls(() => {
const presets = Object.fromEntries(
logo3dPresets.map(({ name, params: { worldWidth, worldHeight, ...preset } }) => [
name,
button(() => {
const { colors, ...presetParams } = preset;
setColors(colors);
setParamsSafe(params, setParams, presetParams);
}),
])
);
return {
Presets: folder(presets, { order: -2 }),
};
});

// 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, logo3dDef, setColors);
usePresetHighlight(logo3dPresets, params);
cleanUpLevaParams(params);

return (
<>
<ShaderContainer shaderDef={logo3dDef} currentParams={params}>
<Suspense fallback={null}>
<Logo3d onClick={handleClick} {...params} colors={colors} image={image} suspendWhenProcessingImage />
</Suspense>
</ShaderContainer>
<ShaderDetails
shaderDef={logo3dDef}
currentParams={{ colors, ...params }}
codeSampleImageName="images/logos/diamond.svg"
/>
</>
);
};

export default Logo3dWithControls;
19 changes: 19 additions & 0 deletions docs/src/shader-defs/logo-3d-def.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { logo3dPresets } from '@paper-design/shaders-react';
import type { ShaderDef } from './shader-def-types';
import { animatedCommonParams } from './common-param-def';

const defaultParams = logo3dPresets[0].params;

export const logo3dDef: ShaderDef = {
name: '3d Logo',
description: 'TBD',
params: [
{
name: 'image',
type: 'HTMLImageElement | string',
description:
'An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes.',
},
...animatedCommonParams,
],
};
4 changes: 4 additions & 0 deletions packages/shaders-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ export { HalftoneCmyk, halftoneCmykPresets } from './shaders/halftone-cmyk.js';
export type { HalftoneCmykProps } from './shaders/halftone-cmyk.js';
export type { HalftoneCmykUniforms, HalftoneCmykParams } from '@paper-design/shaders';

export { Logo3d, logo3dPresets } from './shaders/logo-3d.js';
export type { Logo3dProps } from './shaders/logo-3d.js';
export type { Logo3dUniforms, Logo3dParams } from '@paper-design/shaders';

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

Expand Down
145 changes: 145 additions & 0 deletions packages/shaders-react/src/shaders/logo-3d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { memo, useLayoutEffect, useState } from 'react';
import { ShaderMount, type ShaderComponentProps } from '../shader-mount.js';
import { colorPropsAreEqual } from '../color-props-are-equal.js';
import {
logo3dFragmentShader,
ShaderFitOptions,
defaultObjectSizing,
type Logo3dUniforms,
type Logo3dParams,
toProcessedLogo3d,
type ImageShaderPreset,
getShaderColorFromString,
} from '@paper-design/shaders';
import { transparentPixel } from '../transparent-pixel.js';
import { suspend } from '../suspend.js';

export interface Logo3dProps extends ShaderComponentProps, Logo3dParams {
/**
* Suspends the component when the image is being processed.
*/
suspendWhenProcessingImage?: boolean;
}

type Logo3dPreset = ImageShaderPreset<Logo3dParams>;

export const defaultPreset: Logo3dPreset = {
name: 'Default',
params: {
...defaultObjectSizing,
scale: 0.7,
speed: 1,
frame: 0,
colorBack: '#000000',
colorBase: '#ea05ff',
colors: ['#ffea61', '#00ffee'],
lightsSpread: 0.4,
lightsDiffuse: 1,
lightsSpecular: 1,
lightsShadow: 0.2,
},
};

export const logo3dPresets: Logo3dPreset[] = [defaultPreset];

export const Logo3d: React.FC<Logo3dProps> = memo(function Logo3dImpl({
// Own props
colorBack = defaultPreset.params.colorBack,
colorBase = defaultPreset.params.colorBase,
colors = defaultPreset.params.colors,
speed = defaultPreset.params.speed,
frame = defaultPreset.params.frame,
image = '',
lightsSpread = defaultPreset.params.lightsSpread,
lightsDiffuse = defaultPreset.params.lightsDiffuse,
lightsSpecular = defaultPreset.params.lightsSpecular,
lightsShadow = defaultPreset.params.lightsShadow,
suspendWhenProcessingImage = false,

// Sizing props
fit = defaultPreset.params.fit,
scale = defaultPreset.params.scale,
rotation = defaultPreset.params.rotation,
originX = defaultPreset.params.originX,
originY = defaultPreset.params.originY,
offsetX = defaultPreset.params.offsetX,
offsetY = defaultPreset.params.offsetY,
worldWidth = defaultPreset.params.worldWidth,
worldHeight = defaultPreset.params.worldHeight,
...props
}: Logo3dProps) {
const imageUrl = typeof image === 'string' ? image : image.src;
const [processedStateImage, setProcessedStateImage] = useState<string>(transparentPixel);

let processedImage: string;

if (suspendWhenProcessingImage && typeof window !== 'undefined' && imageUrl) {
processedImage = suspend(
(): Promise<string> => toProcessedLogo3d(imageUrl).then((result) => URL.createObjectURL(result.pngBlob)),
[imageUrl, 'logo3d']
);
} else {
processedImage = processedStateImage;
}

useLayoutEffect(() => {
if (suspendWhenProcessingImage) {
// Skip doing work in the effect as it's been handled by suspense.
return;
}

if (!imageUrl) {
setProcessedStateImage(transparentPixel);
return;
}

let url: string;
let current = true;

toProcessedLogo3d(imageUrl).then((result) => {
if (current) {
url = URL.createObjectURL(result.pngBlob);
setProcessedStateImage(url);
}
});

return () => {
current = false;
};
}, [imageUrl, suspendWhenProcessingImage]);

const uniforms = {
// Own uniforms
u_colors: colors.map(getShaderColorFromString),
u_colorsCount: colors.length,
u_colorBack: getShaderColorFromString(colorBack),
u_colorBase: getShaderColorFromString(colorBase),
u_image: processedImage,
u_lightsSpread: lightsSpread,
u_lightsDiffuse: lightsDiffuse,
u_lightsSpecular: lightsSpecular,
u_lightsShadow: lightsShadow,

// Sizing uniforms
u_fit: ShaderFitOptions[fit],
u_scale: scale,
u_rotation: rotation,
u_offsetX: offsetX,
u_offsetY: offsetY,
u_originX: originX,
u_originY: originY,
u_worldWidth: worldWidth,
u_worldHeight: worldHeight,
} satisfies Logo3dUniforms;

return (
<ShaderMount
{...props}
speed={speed}
frame={frame}
fragmentShader={logo3dFragmentShader}
mipmaps={['u_image']}
uniforms={uniforms}
/>
);
}, colorPropsAreEqual);
8 changes: 8 additions & 0 deletions packages/shaders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ export {
type HalftoneCmykType,
} from './shaders/halftone-cmyk.js';

export {
logo3dMeta,
logo3dFragmentShader,
toProcessedLogo3d,
type Logo3dParams,
type Logo3dUniforms,
} from './shaders/logo-3d.js';

// ----- Utils ----- //
export { getShaderColorFromString } from './get-shader-color-from-string.js';
export { getShaderNoiseTexture } from './get-shader-noise-texture.js';
Expand Down
Loading