Halftone Portrait
Interactive image dots with photo detail, colour controls and a responsive cursor.
Install the component, then paste your configured usage. Your settings are included in the code below.
npx arclen-ui add halftone-portrait --settings eyJzcmMiOiJlY2xpcHNlIiwiYWx0IjoiQXN0cm9uYXV0IGluIGZyb250IG9mIGFuIGVjbGlwc2UiLCJzcGFjaW5nIjoyLCJzaXplIjoxLCJ2YXJpYXRpb24iOjAuMSwidGhyZXNob2xkIjowLjEsImNvbnRyYXN0IjoxLjE1LCJleHBvc3VyZSI6MS4xNSwiYWxwaGFHYW1tYSI6MC41LCJvcGFjaXR5IjoxLCJjb2xvciI6IiNmZmZmZmYiLCJhY2NlbnQiOiIjZjU0ZTAwIiwiYWNjZW50QW1vdW50IjowLCJjb2xvck1vZGUiOiJkdW90b25lIiwiZml0IjoiY29udGFpbiIsInNjYWxlIjoxLCJmb2NhbFgiOjAuNSwiZm9jYWxZIjowLjUsInJhZGl1cyI6MTgwLCJmb3JjZSI6MzIsInJldHVyblNwZWVkIjowLjE2LCJjdXJzb3JTbW9vdGhpbmciOjAuMjIsInJlbGVhc2VTcGVlZCI6MC4xMiwiZmFsbG9mZiI6MiwiY3Vyc29yTW9kZSI6InJlcGVsIiwic2hvd0N1cnNvciI6ZmFsc2UsImN1cnNvckNvbG9yIjoiI2ZmZmZmZiIsIm1vdGlvbiI6dHJ1ZSwiYmFja2dyb3VuZCI6InRyYW5zcGFyZW50IiwiYm9yZGVyUmFkaXVzIjowLCJmYWRlIjowLCJhc3BlY3RSYXRpbyI6MC44fQimport { HalftonePortrait } from "@/components/halftone-portrait";
export default function Example() {
return <HalftonePortrait {...{
"src": "eclipse",
"alt": "Astronaut in front of an eclipse",
"spacing": 2,
"size": 1,
"variation": 0.1,
"threshold": 0.1,
"contrast": 1.15,
"exposure": 1.15,
"alphaGamma": 0.5,
"opacity": 1,
"color": "#ffffff",
"accent": "#f54e00",
"accentAmount": 0,
"colorMode": "duotone",
"fit": "contain",
"scale": 1,
"focalX": 0.5,
"focalY": 0.5,
"radius": 180,
"force": 32,
"returnSpeed": 0.16,
"cursorSmoothing": 0.22,
"releaseSpeed": 0.12,
"falloff": 2,
"cursorMode": "repel",
"showCursor": false,
"cursorColor": "#ffffff",
"motion": true,
"background": "transparent",
"borderRadius": 0,
"fade": 0,
"aspectRatio": 0.8
}} />;
}| Property | Type | Default |
|---|---|---|
srcArtwork or image | image | "eclipse" |
altImage description | text | "Astronaut in front of an eclipse" |
spacingDot spacing 2–12Step 1 | number | 2 |
sizeDot size 0.5–4Step 0.01 | number | 1 |
variationSize variation 0–1Step 0.01 | number | 0.1 |
thresholdShadow cutoff 0–0.9Step 0.01 | number | 0.1 |
contrastContrast 0.25–4Step 0.01 | number | 1.15 |
exposureExposure 0.25–3Step 0.01 | number | 1.15 |
alphaGammaTonal curve 0.2–2.5Step 0.01 | number | 0.5 |
opacityOpacity 0–1Step 0.01 | number | 1 |
colorDot colour | color | "#ffffff" |
accentAccent colour | color | "#f54e00" |
accentAmountAccent mix 0–1Step 0.01 | number | 0 |
colorModeColour source Options: duotone, original, photo (Photo (full detail)) | select | "duotone" |
fitImage fit Options: cover, contain | select | "cover" |
scaleZoom 0.4–3Step 0.01 | number | 1 |
focalXHorizontal crop 0–1Step 0.01 | number | 0.5 |
focalYVertical crop 0–1Step 0.01 | number | 0.5 |
radiusInfluence radius 20–500Step 1 | number | 180 |
forceStrength 0–140Step 1 | number | 32 |
returnSpeedParticle response 0.02–1Step 0.01 | number | 0.16 |
cursorSmoothingCursor tracking 0.02–1Step 0.01 | number | 0.22 |
releaseSpeedRelease response 0.02–1Step 0.01 | number | 0.12 |
falloffEdge softness 1–5Step 0.01 | number | 2 |
cursorModeCursor behavior Options: repel, attract, swirl | select | "repel" |
showCursorCursor ring | boolean | false |
cursorColorRing colour | color | "#ffffff" |
motionInteraction | boolean | true |
backgroundBackground | color | "transparent" |
borderRadiusCorner radius 0–100Step 1 | number | 0 |
fadeBottom fade 0–0.7Step 0.01 | number | 0 |
aspectRatioAspect ratio 0.3–3Step 0.01 | number | 0.8 |
Halftone Portrait
A Canvas2D portrait with sampled dots and local pointer interaction. Eclipse is the default astronaut artwork. Ronin, Eclipse and Sentinel artwork are included in every install. There are no requests to the source website.
React
Import the named component from the installed component directory. The entry includes its CSS. Give image and background components a sized container; the wrapper accepts style and className.
import { HalftonePortrait } from "./components/halftone-portrait";
<HalftonePortrait src="eclipse" fit="contain" style={{height: 520}} />Use regular props for every setting in the generated props table. paused suspends motion without discarding settings. Unmount releases observers, event listeners and animation resources.
HTML and vanilla
Copy the complete HTML bundle, including its assets, to your static host. Use the supplied component.html markup with component.css and component.js. Relative bundle URLs work under a subpath. The bundle initializes each instance independently from its serialized settings.
For a custom integration, import render, mount and resolveSettings from src/core. render(settings) is safe during server rendering. Pass an outer host element to mount(host, settings), then use update(settings), pause(), resume() and destroy() on the returned controller. Always destroy a controller before removing its host.
Accessibility and fallback
Arrow keys move the interaction target; Shift increases the step and Escape resets it. Reduced motion keeps the image static. The original image remains available without Canvas or JavaScript, and no WebGL context is required. For local image upload, create an object URL, pass it as src, and revoke it when replaced or unmounted. Uploaded images never leave the browser. For hosted images, use a same-origin URL or a server that grants image CORS access.
Assets and rights
See component.json for the complete asset list and assets/NOTICE.md for provenance. All source is standalone. No Arclen application runtime, authentication, database code, commercial fonts or licensed icon packs are required.
Performance
The default transparent background lets the containing surface supply contrast; use a dark surface for the default white dots, or set background="#000000". Dot spacing and size stay at their authored values on every host size, including high-density displays. Large portraits with close dot spacing require more rendering work; the component repaints the area affected by pointer movement, sleeps at rest and suspends offscreen without reducing image detail. Initial images, resized canvases and image setting changes repaint in full. Interaction-only settings reuse the existing samples. On surfaces above one million CSS pixels, image setting changes first show an accessible “Rendering artwork” status, keep the previous full-detail frame visible and then rebuild at full quality. Rapid image edits share the pending render job. A home-coordinate index limits pointer updates to affected dots. Changed shapes draw on a second canvas before their pixels are copied to the visible canvas, preserving overlap order and antialiasing without expanding the repaint area across the whole portrait. This uses one additional canvas at the same resolution. The component has no npm runtime dependencies. The release gate records the compressed bundle size, startup and interaction timing. Reduced motion and offscreen suspension take priority over continuous effects.
The preview runs the component’s own JavaScript. It respects reduced motion and pauses when off screen. The HTML bundle includes a poster fallback.
Every setting in this panel comes from the component manifest. Preview size, pause and replay stay in the inspector.