Parser library for Koikatu / Honeycome character cards and scene data. Extracts data from the binary payload appended after the PNG IEND chunk.
Works in both browser and Node.js. ESM / CJS / IIFE output.
npm install koikatu.js<script src="https://cdn.jsdelivr.net/npm/koikatu.js/dist/index.global.min.js"></script>
<script>
const { parseCardSummary } = KoikatuJS;
</script>The global variable name is KoikatuJS.
import { parseCard, parseCardSummary, isCard } from 'koikatu.js';
// Check if a file is a character card
const buf = await fetch('card.png').then(r => r.arrayBuffer());
if (isCard(buf)) {
console.log('Character card detected');
}
// Full parse
const card = parseCard(buf);
console.log(card.header.header); // "【KoiKatuChara】"
console.log(card.blockIndex); // [{ name: "Custom", ... }, ...]
console.log(card.blocks['Parameter']); // { lastname: "白峰", firstname: "一乃", ... }
// Summary only
const summary = parseCardSummary(buf);
console.log(summary.product); // "【KoiKatuChara】"
console.log(summary.name); // "白峰 一乃"
console.log(summary.birthday); // { month: 5, day: 12 }import {
parseHcScene,
parseKkScene,
serializeHcScene,
serializeKkScene,
} from 'koikatu.js';
const kkScene = parseKkScene(await fetch('scene.png').then(r => r.arrayBuffer()));
console.log(kkScene.version);
console.log(Object.keys(kkScene.objects).length);
const hcScene = parseHcScene(await fetch('hc_scene.png').then(r => r.arrayBuffer()));
console.log(hcScene.title);
const kkBytes = serializeKkScene(kkScene);
const hcBytes = serializeHcScene(hcScene);Convert a character card from one supported title to another. Only conversions within the same series are supported (KK series: KK / KKS / EC; HC series: HC / SV / AC). Cross-series conversion (e.g. KK → HC) is not supported.
One-shot — convertCard parses, converts, and serializes in a single call:
import { convertCard } from 'koikatu.js';
const buf = await fetch('kk_card.png').then(r => r.arrayBuffer());
// Convert a Koikatu card to Koikatu Sunshine
const kksBytes = convertCard(buf, 'KKS');
// Convert a Honeycome card to Summer Vacation
const svBytes = convertCard(buf, 'SV');Step-by-step — use transformCard + serializeCard when you need to inspect or modify the card between conversion and serialization:
import { parseCard, transformCard, serializeCard, scanPngIend } from 'koikatu.js';
const buf = new Uint8Array(await fetch('kk_card.png').then(r => r.arrayBuffer()));
const card = parseCard(buf);
// Extract the original PNG image bytes (required by some target formats)
const pngBytes = buf.slice(0, scanPngIend(buf));
const converted = transformCard(card, 'EC', { pngBytes });
const result = serializeCard(converted, pngBytes);options.pngBytes is required for conversions that embed the thumbnail image in the payload (EC → KK / KKS, HC ↔ SV, HC ↔ AC).
Saving the result (Node.js)
import { writeFile } from 'node:fs/promises';
const result = convertCard(buf, 'KKS');
await writeFile('converted.png', result);Downloading the result (browser)
const result = convertCard(buf, 'KKS');
const blob = new Blob([result], { type: 'image/png' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'converted.png';
a.click();
URL.revokeObjectURL(url);const card = parseCard(rawPayload, { containsPng: false });Fully parses a card and returns the header, all decoded blocks, and the block index.
Parses a card and returns a normalized summary including product, name, birthday, and sex. product currently mirrors header.header.
Lightweight parse of the header only.
Returns whether the input is a character card.
Returns the byte offset immediately after the PNG IEND chunk.
Transforms a parsed card to another supported title format. Use options.pngBytes when the target format needs image data.
Serializes a transformed Card back into PNG + payload bytes.
Runs parseCard -> transformCard -> serializeCard in one step.
Parses Koikatu / Koikatu Sunshine scene data.
Serializes a KkScene back into PNG + payload.
Parses Honeycome / SummerVacation / Aicomi scene data. Encrypted or unknown HC tail blocks are preserved as raw bytes.
Serializes an HcScene back into PNG + payload.
type ConvertTarget = 'KK' | 'KKS' | 'EC' | 'HC' | 'SV' | 'AC' | 'AL';Low-level helpers such as BinaryReader, decodeMsgpack, and block decoders are also exported.
type ParseOptions = {
containsPng?: boolean; // default: true - whether the input contains a PNG image
strict?: boolean; // default: false - throw on unknown headers or corrupted blocks
decodeBlocks?: boolean; // default: true - decode blocks via MessagePack
};
type SceneParseOptions = {
containsPng?: boolean; // default: true
strict?: boolean; // default: false
decodeEmbeddedCards?: boolean; // default: true
preserveRaw?: boolean; // default: false - keep original scene bytes for exact round-trip
};| Game | Header |
|---|---|
| Koikatu | 【KoiKatuChara】 |
| Koikatu Sunshine | 【KoiKatuCharaSun】 |
| EmotionCreators | 【EroMakeChara】 |
| Honeycome | 【HCChara】 |
| Honeycome Party | 【HCPChara】 |
| DigitalCraft | 【DCChara】 |
| SummerVacation | 【SVChara】 |
| Aicomi | 【ACChara】 |
| Amanatsu Location | 【ALChara】 |
【KoiKatuCharaSP】 is also accepted as a Koikatu-compatible header.
type ParseError = {
code: string;
message: string;
at?: string;
};
type MsgpackHint =
| { kind: 'number'; format: 'int' | 'float' }
| { kind: 'array'; items: MsgpackHint[] }
| {
kind: 'map';
entries: {
keyId: string;
keyType: 'string' | 'int' | 'float' | 'boolean' | 'nil';
valueHint: MsgpackHint;
}[];
}
| { kind: 'scalar' };
type Card = {
header: CardHeader;
blocks: Record<string, any>;
blockIndex: BlockInfo[];
rawBlockBytes?: Record<string, Uint8Array>;
blockHints?: Record<string, MsgpackHint>;
errors?: ParseError[];
unsupportedHeader?: boolean;
};
type CardSummary = {
header: CardHeader;
product: string;
name?: string;
birthday?: { month?: number; day?: number };
sex?: number;
hasKKEx?: boolean;
blocks: string[];
};
type CardHeader = {
productNo: number;
header: string;
version: string;
faceImage?: Uint8Array;
language?: number;
userid?: string;
dataid?: string;
packages?: number[];
};
type BlockInfo = {
name: string;
version: string;
pos: number;
size: number;
};make install # Install dependencies
make build # Build with tsup
make test # Run tests
make check # Run lint + build + test
make clean # Remove dist/make build emits both dist/index.global.js and dist/index.global.min.js for CDN / classic <script> usage.
Looking for a Python library? Check out KoikatuCharaLoader.
MIT