Embed interactive clickable hotspots into PNG images.
Draw once with the Editor, render everywhere with the SDK.
English | 简体中文
- Draw hotspots on an image using the visual Editor
- Export as a standard PNG with hotspot metadata embedded in
tEXtchunks - Render in your app — the SDK reads the metadata and creates clickable zones automatically
No server required. All data lives inside the PNG file itself.
Use the online editor to create and manage hotspots on your images:
https://clickable-img.dev.noteloom.app
The editor lets you visually draw hotspot regions on any PNG image, add labels and custom data to each hotspot, then export the image with all hotspot metadata embedded. The exported PNG can be used directly with the SDK — no additional configuration files needed.
npm install @clickable-img/sdkimport { ClickableImg } from '@clickable-img/sdk'
const img = document.querySelector('img#hero')
const instance = await ClickableImg.attach(img, {
onClick(hotspot) {
console.log(hotspot.label) // "Buy Now"
console.log(hotspot.payload) // "product-123"
},
showHints: true, // optional — show hotspot borders
})
// Read custom data embedded in the image
const customData = instance.getCustomData() // string | null
// clean up when done
instance.destroy()clickable-img works on any JavaScript-based platform. The @clickable-img/sdk is a ready-to-use wrapper for the browser, but it's not required — all you need is @clickable-img/core.
- Read hotspot data — Call
readHotspotsFromPngfrom the core package to parse the PNG and extract hotspot coordinates and custom data. - Build your own overlay — Use the hotspot coordinates to create clickable regions on top of the image using your platform's native approach — Canvas, native Views, WebGL, etc.
- Handle click events — When a user taps a hotspot, read its
labelandpayloadto execute your business logic.
The core package has zero external dependencies and is pure JavaScript — it runs in Node.js, React Native, Mini Programs, Electron, and any other JS runtime.
For platforms without a built-in adapter (React Native, Flutter, Node.js, etc.):
npm install @clickable-img/coreimport { readHotspotsFromPng, readCustomDataFromPng } from '@clickable-img/core'
const buffer = await fetch('https://example.com/image.png')
.then(r => r.arrayBuffer())
const data = readHotspotsFromPng(buffer)
if (data) {
data.hotspots.forEach(hotspot => {
// hotspot.rect -> { x, y, w, h } in 0~1 range (relative to image size)
// hotspot.label, hotspot.payload, hotspot.id
})
// Read custom data embedded in the image
console.log(data.customData) // string | undefined
}
// Or read custom data directly without parsing hotspots
const customData = readCustomDataFromPng(buffer) // string | nullWhen you need interactive regions on images, clickable-img provides the lightest solution:
- Ad Pop-ups — Buttons and product zones in pop-up ads are configured as hotspots. Swap the image to update content — zero code changes.
- Campaign Landing Pages — Use the designer's artwork directly as the page. Hotspots handle navigation, eliminating complex CSS reproduction.
- Interactive Maps — Scenic area guides, mall floor plans, etc. Mark points of interest on images, click to reveal details.
- Interactive Learning — Mark knowledge points on educational images. Students click to view explanations.
- Develop once — Build the image pop-up component, integrate the SDK, and configure hotspot click event handlers.
- Configure hotspots — Operations team uses the online editor to draw interactive hotspots on the new ad image.
- Deploy content — Upload the exported image to CDN / object storage, replacing the old image URL.
Result: No code changes, no redeployment — ad content is hot-updated instantly.
- Hot Update — Replace the image to ship new content, no release needed
- Fast Delivery — No need to reproduce complex UI designs in code; a single image carries the entire visual
- Low Dev Cost — Display-oriented pages only need click event listeners, drastically reducing frontend work
All hotspot data is stored inside the PNG file itself — no extra server required.
The PNG specification defines tEXt auxiliary chunks for storing text metadata. The editor serializes hotspot data as JSON, writes it into a tEXt chunk with the keyword clickable-img, and inserts it before the IEND marker. Pixel data remains completely untouched.
Hotspot coordinates use relative values in the 0~1 range (percentage of image width/height). This means hotspots always align precisely, regardless of the actual rendered image size on screen.
At runtime, the SDK fetches the image binary data, parses the tEXt chunk to extract the hotspot JSON, wraps the <img> element in a position: relative container, and generates an position: absolute transparent <div> for each hotspot with click event listeners.
Editor draws hotspots → serialize to JSON
↓
JSON written into PNG tEXt chunk → image exported
↓
SDK loads image → parses tEXt chunk
↓
DOM overlay generated from coordinates → interaction events bound
| Package | Description | Platform |
|---|---|---|
@clickable-img/core |
Type definitions & PNG metadata parser | Any JS runtime |
@clickable-img/sdk |
Browser runtime with DOM overlay | Web |
@clickable-img/editor |
Visual hotspot editor | Web |
| Parameter | Type | Description |
|---|---|---|
img |
HTMLImageElement |
Target image element |
options.onClick |
(hotspot: Hotspot) => void |
Callback when a hotspot is clicked |
options.showHints |
boolean |
Show hotspot borders. Default: false |
Returns Promise<ClickableImg>. The instance provides:
.getCustomData(): string | null— Returns custom data embedded in the image, ornullif none..destroy()— Removes the overlay and cleans up.
Parses PNG tEXt chunks and returns all embedded data (hotspots + custom data), or null if none found.
Convenience method to read only the custom data field from a PNG, without needing to process hotspots.
interface ClickableImgData {
version: string
hotspots: Hotspot[]
customData?: string // arbitrary text embedded with the image
}
interface Hotspot {
id: string
rect: HotspotRect
label: string
payload: string
}
interface HotspotRect {
x: number // 0~1, left offset relative to image width
y: number // 0~1, top offset relative to image height
w: number // 0~1, width relative to image width
h: number // 0~1, height relative to image height
}pnpm install # install dependencies
pnpm dev # start all packages in watch mode
pnpm build # build all packagesContributions are welcome! Please open an issue first to discuss what you'd like to change.