inputism
v0.0.4
Published
Turn images into checkbox mosaics
Readme
Inputism
Inputism turns images into checkbox mosaics. The preferred way to display that
model is the <inputism-image> web component.
The library keeps these concerns separate:
- An input source provides RGBA pixels.
- The core creates an
InputismImagefrom those pixels. - A renderer displays the model.
This means an application can use a URL, a data URL, a file decoder, or any other source that produces RGBA data without changing the image model.
Install
npm install inputismQuick start
Load the web-component entry once. It registers <inputism-image> and
includes the default styles:
<script type="module" src="https://esm.sh/inputism/element"></script>
<inputism-image
src="/cat.jpg"
density="36"
mark="indeterminate"
label="A cat"
></inputism-image>The component accepts these attributes:
| Attribute | Values | Purpose |
| --- | --- | --- |
| src | URL, data URL, or blob URL | Image source to decode |
| density | Positive integer | Number of columns |
| mark | checked, indeterminate, or background | Cell appearance |
| max-rows | Positive integer | Upper limit for generated rows |
| crossorigin | CORS mode | Request mode for remote images |
| label | Text | Accessible name for the internal image |
| loading | lazy | Wait until the element approaches the viewport |
To reserve space before a source image loads, provide its dimensions. The
component uses them as a fallback aspect ratio; surrounding CSS can still set
its own aspect-ratio or width.
<inputism-image
src="/cat.jpg"
width="640"
height="480"
></inputism-image>label is a component-specific attribute. The component applies
role="img" to its internal grid and uses label as that role's
aria-label, similar to the relationship between <img> and alt. The
attribute is not applied to the host element. If it is omitted, the internal
image has no accessible name.
Changing src, density, or max-rows loads the source again. Changing
mark updates the rendered cells. Without loading="lazy", sources load as
soon as the component connects. Lazy sources use IntersectionObserver and
load when they are near the viewport.
Supplying encoded image data
src also accepts a base64 data URL. This is useful when the application
already has an encoded image:
const element = document.querySelector("inputism-image");
element?.setAttribute("src", encodedImageDataUrl);Remote URLs must allow anonymous CORS access. If the server is configured for
credentialed requests, use crossorigin="use-credentials".
Supplying RGBA data
When the application already has pixels, create the shared model directly and
assign it to the component's image property:
import { createInputismImage } from "inputism/core";
import type { InputismElement } from "inputism/element";
const image = createInputismImage(
{
width: pixelsWidth,
height: pixelsHeight,
data: rgbaPixels,
},
{
density: 36,
mark: "indeterminate",
},
);
const element = document.querySelector<InputismElement>("inputism-image");
if (element) {
element.image = image;
}RgbaImage.data is row-major RGBA data: four values per source pixel in
red, green, blue, alpha order. The core downsamples those pixels into the
requested layout.
Handling load and errors
After a src image has loaded, been converted, and rendered, the component
emits a bubbling inputism-load event. InputismElement includes the custom
event type, so the converted InputismImage model is available as the typed
event.detail:
import "inputism/element";
import type { InputismElement } from "inputism/element";
const element = document.querySelector<InputismElement>("inputism-image");
const output = document.querySelector<HTMLElement>("[data-load-status]");
element?.addEventListener("inputism-load", (event) => {
const image = event.detail;
if (output) {
output.textContent = `Loaded ${image.columns} × ${image.rows} cells.`;
}
});If a src cannot be loaded, the component does not render an error message. It
instead emits a bubbling inputism-error event; the original error is
available as event.detail:
import "inputism/element";
import type { InputismElement } from "inputism/element";
const element = document.querySelector<InputismElement>("inputism-image");
element?.addEventListener("inputism-error", (event) => {
const error = event.detail;
console.error("Inputism could not load the image", error);
});Core model
Use inputism/core when layout creation and pixel sampling need to be
controlled separately:
import {
createInputismColors,
createInputismLayout,
} from "inputism/core";
const layout = createInputismLayout(width, height, {
density: 36,
mark: "indeterminate",
});
const colors = createInputismColors(rgbaImage, layout);The layout describes the cell coordinate system:
{
columns: 36,
rows: 24,
mark: "indeterminate",
}InputismColors stores one RGB triplet per cell in a flat
Uint8ClampedArray. Its length is columns * rows * 3.
The one-step helper returns both parts as an InputismImage:
import { createInputismImage } from "inputism/core";
const image = createInputismImage(rgbaImage, {
density: 36,
mark: "checked",
});Source adapters
Use inputism/source to keep image acquisition separate from pixel
transformation:
import {
createInputismImageFromSource,
type ImageDataSource,
} from "inputism/source";
const source: ImageDataSource = async () => ({
width: pixelsWidth,
height: pixelsHeight,
data: rgbaPixels,
});
const image = await createInputismImageFromSource(source, {
density: 36,
mark: "checked",
});For browser image URLs, data URLs, and blob URLs, use the included adapter:
import { createInputismImageFromUrl } from "inputism/source";
const image = await createInputismImageFromUrl("/cat.jpg", {
density: 36,
});HTML renderer
inputism/html is the lower-level renderer used by the web component. It
creates the grid structure, applies cell colors, and returns the styles needed
to display that structure:
import { createInputismHtml } from "inputism/html";
const view = createInputismHtml(container, layout);
view.setColors(colors);
view.setColors(nextColors);Set inlineStyles: true when the exported HTML should carry its visual rules
directly on the grid and inputs:
createInputismHtml(container, layout, { inlineStyles: true });With the default options, use view.styles to build a stylesheet for the
generated classes. The web component uses this same style object inside its
shadow root.
For a complete model, use the one-step helper:
import { renderInputismHtml } from "inputism/html";
renderInputismHtml(container, image, {
mark: "checked",
inlineStyles: true,
});Web-component exports
The inputism/element entry registers the element when imported:
import { defineInputismElement } from "inputism/element";
defineInputismElement();It also exports InputismElement for applications that need the element class
directly.
