A browser-based Chinese calligraphy tattoo design tool — style, verify, preview on skin, and export before committing to ink.
Live: haninkcalli.com
HanInk lets users type or select a Chinese character, style it with one of twelve historically-grounded calligraphy fonts, preview it on an interactive 3D body model, save designs to the cloud, and export a print-ready image. A built-in meaning/pronunciation lookup and a permanent verification warning address a real pain point: people getting tattoos of characters they don't fully understand.
Chinese character tattoos are popular worldwide, but mistranslations and style mismatches are common. HanInk solves this by putting accurate cultural context alongside a live visual preview — all in the browser, with no sign-up required for the core tool.
| Feature | Details |
|---|---|
| 12 authentic script styles | 5 classical scripts (Kaishu, Xingshu, Caoshu, Lishu, Zhuanshu) + 7 master calligraphers (Wang Xizhi, Yan Zhenqin, Mi Fu, Huang Tingjian, Song Huizong, Qi Gong, Mao Zedong) |
| Auto-translate | Type English → Google Translate returns Chinese automatically (800 ms debounce, up to 20 characters) |
| Full customization | Font size (32–180 px in scroll view / 0.8–5.0 cm in tattoo view), writing direction (horizontal/vertical), character spacing, stroke weight |
| Interactive 3D body mockup | Full 3D model (male/female) rendered with Three.js + React Three Fiber — click anywhere on the skin to place and rotate the tattoo; multiple skin tone options |
| Save Inks | Authenticated users save and reload designs via AWS Amplify + DynamoDB |
| User Profile | Username setting, saved ink count, account management |
| Content pages | Celebrity ink showcase, ancient Chinese wisdom phrases, per-style editorial pages |
| Export | Transparent PNG, SVG with embedded fonts, shareable deep-link URL, and copy-to-clipboard |
| Layer | Choice |
|---|---|
| Framework | Next.js 16.2.2 (App Router, Webpack mode) |
| UI | React 19 · TypeScript 5 |
| Styling | Tailwind CSS v4 |
| 3D | Three.js · @react-three/fiber · @react-three/drei |
| Auth / DB | AWS Amplify v6 (Cognito + DynamoDB) |
| Fonts | Inter & LXGW WenKai TC (Google Fonts) + 12 local WOFF2/TTF calligraphy fonts |
| Analytics | Google Analytics (GA4) |
HanInk/
├── app/
│ ├── layout.tsx # Root layout — fonts, metadata, GA, AmplifyProvider
│ ├── page.tsx # Home → <CalligraphyApp />
│ ├── globals.css # Tailwind v4 + custom classes
│ ├── profile/page.tsx # User profile (auth-gated)
│ ├── celebrity-ink/page.tsx # Celebrity Chinese tattoo showcase
│ ├── ancient-wisdom/page.tsx # Classical Chinese wisdom phrases
│ ├── styles/
│ │ ├── page.tsx # All 12 calligraphy styles overview
│ │ └── [style]/page.tsx # Individual style editorial page
│ ├── tattoo/
│ │ ├── page.tsx
│ │ └── [keyword]/page.tsx
│ ├── components/
│ │ ├── CalligraphyApp.tsx # Main app shell — all state, layout, font injection
│ │ ├── CalligraphyCanvas.tsx # Live canvas rendering selected font
│ │ ├── StylePicker.tsx # Style tabs + master calligraphers accordion
│ │ ├── CustomizeControls.tsx # Size, spacing, direction, stroke weight
│ │ ├── TattooPreview.tsx # 3D body placement view
│ │ ├── BodyPart3D.tsx # Three.js scene, OrbitControls, raycasting
│ │ ├── ExportPanel.tsx # PNG / SVG / clipboard / share URL
│ │ ├── SavedInksPanel.tsx # Cloud-saved designs (requires auth)
│ │ ├── Header.tsx
│ │ ├── Footer.tsx
│ │ ├── AmplifyProvider.tsx # Amplify + Authenticator wrapper
│ │ └── ScrollToTop.tsx
│ ├── data/
│ │ ├── characters.ts # Character library + famous quotes (celebrity & ancient)
│ │ ├── styles.ts # styleOptions array (id, fontFamily, description)
│ │ ├── style-content.ts # Extended per-style editorial content
│ │ ├── character-content.ts
│ │ └── constants.ts # SKIN_TONE_OPTIONS, FONT_FILES map
│ ├── types/index.ts # ScriptStyle, WritingDirection, SkinTone, LineWeight, Gender
│ └── lib/
│ ├── amplifyClient.ts # Amplify data client (SavedInk, UserProfile models)
│ └── authTheme.ts # Amplify Authenticator custom theme
├── amplify/
│ ├── auth/ # Cognito config
│ ├── data/ # DynamoDB schema (SavedInk, UserProfile)
│ └── backend.ts
└── public/fonts/ # 12 calligraphy WOFF2 + TTF files
- Font loading strategy — KaiShu, LiShu, WangXiZhi, MaoZeDong, ZhuanShu, and MiFu are preloaded in
layout.tsx. Script-style fonts are lazy-injected viarequestIdleCallbackafter first paint. Master calligrapher fonts only load when the accordion opens. - State & persistence — URL params (
?t=&s=&d=&fs=&lw=&cs=) are consumed once and cleared.localStorage(hanink_design) persists the last design. AWS Amplify stores named designs for authenticated users. - 3D placement —
TattooPreview.tsxuses React Three Fiber +useGLTFto load gender-specific GLB models. A raycast hit returns aSerializedHit(3D point + normal) that drives the overlay position. - Vertical text rendering — CSS
writing-mode: vertical-rlwithtext-orientation: mixedhandles CJK vertical layout natively. - SVG export with font embedding —
ExportPanel.tsxfetches and base64-encodes the font file, embedding it directly in the SVG for portable rendering. - Type-safe data layer — Shared types in
app/types/index.tskeep component props, character data, and persistence in sync at compile time.
| ID | Display Name | Font |
|---|---|---|
kaishu |
Regular Script (楷书) | KaiShu — preloaded |
xingshu |
Running Script (行书) | XingShu |
caoshu |
Cursive Script (草书) | CaoShu |
lishu |
Clerical Script (隶书) | LiShu — preloaded |
zhuanshu |
Seal Script (篆书) | ZhuanShu — preloaded |
wangxizhi |
Wang Xizhi (王羲之) | WangXiZhi — preloaded |
yanzhenqin |
Yan Zhenqin (颜真卿) | YanZhenQin |
mifu |
Mi Fu (米芾) | MiFu — preloaded |
huangtingjian |
Huang Tingjian (黄庭坚) | HuangTingJian |
songhuizong |
Song Huizong (宋徽宗) | SongHuiZong |
qigong |
Qi Gong (启功) | QiGong |
maozedong |
Mao Zedong (毛泽东) | MaoZeDong — preloaded |
npm install
npm run dev # http://localhost:3000Build for production:
npm run build
npm run startAnalyze bundle:
npm run analyzeNote: All scripts pass
--webpackexplicitly. Do not omit this flag — the project does not use Turbopack.
Deployed on AWS Amplify with a custom domain managed via AWS Route 53.
- Every push to the
developbranch triggers an automatic build and redeploy via GitHub webhook - Assets served globally through CloudFront CDN with SSL auto-provisioned via ACM
- Domain: haninkcalli.com


