Giting.
낮의 Giting

홈 · 마스코트 · 스프라이트 · React 컴포넌트 · Claude Code 스킬 · 이미지 생성

page-mascot 대표 이미지 f9c92d7 (2026-09-14, 전체 5커밋)
분석 리포트실측 정보사이트적용점원문 README

하루 만에 333스타, 그런데 커밋은 5개

page-mascot은 커서를 따라 고개를 돌리고 클릭하면 눈을 깜빡이는, 웹 페이지에 붙이는 캐릭터 컴포넌트다. 전체 커밋 5개가 전부 2026-09-14 하루에 찍혔고 저자는 한 명이다. 그런데 스타가 333개다. 이유는 저자가 Kamran Ahmed, 수십만 스타 규모의 roadmap.sh를 만든 사람이라서다. 즉 이 숫자는 코드가 쌓아 올린 신뢰가 아니라 저자 이름이 하루 만에 끌어온 관심이다. 실측과 화제성을 분리해서 읽어야 하는 전형적인 사례다.

구조: 두 장의 시트, 프레임 JS 없음

캐릭터 한 종은 두 장의 3×3 스프라이트다. 한 장은 머리 방향 9칸, 다른 장은 표정 9칸. 포인터의 각도가 방향 시트의 셀을 고르고(커서가 가까우면 머리가 멈추는 데드존이 있다), 클릭하면 반응 시트의 셀이 0.5초 보인다. 셀 전환은 CSS background-position만 옮기는 것이라 프레임마다 도는 자바스크립트도, 애니메이션 라이브러리도 없다. 런타임 의존성이 0인 이유다. 컴포넌트에 넘기는 것은 시트 두 장의 경로뿐이고, 캐릭터에 이름을 등록하는 레지스트리도 없다. 기성 캐릭터 52종을 colour·ink·sketch·riso·paper·pixel 여섯 스타일로 그려 두었는데, 렌더 방식만 바뀌므로 정렬은 그대로 유지된다. 말보다 만져보는 게 빠르다. 아래는 그 동작을 Giting이 직접 만든 데모다. 마우스를 움직이면 시선이 따라오고, 클릭하면 반응한다.

page-mascot의 동작을 Giting이 직접 만든 데모로 옮겼습니다. 무대 위에서 마우스를 움직이면 시선이 따라오고, 무대를 클릭하면 반응합니다. 커서가 가까우면 시선이 가운데로 settle(데드존)합니다. 이 마스코트는 우리 SVG·JS이며, page-mascot의 스프라이트·코드는 복제하지 않았습니다.

live demo · 커서 추적 커서를 움직여 보세요 · 클릭하면 반응

이 데모가 재현한 것

시선포인터 각도가 눈(과 살짝 고개)을 정한다. 실제 제품은 9방향 시트에서 셀을 고른다.
반응클릭하면 0.5초 눌리며 표정이 바뀐다. 실제 제품은 9표정 시트의 셀을 보여준다.
데드존커서가 가까우면 시선이 가운데로 가라앉아 떨림이 없다.
실제 page-mascot은 두 장의 3×3 스프라이트를 background-position으로 스왑해 이걸 프레임 JS 없이 해낸다. 이 데모는 원리를 보여주려 SVG로 다시 만든 것이다.

진짜 값어치는 파이썬 파이프라인에 있다

이 저장소가 흥미로운 건 눈에 보이는 위젯이 아니라, skills/page-mascot에 함께 실린 Claude Code 스킬과 다섯 개의 파이썬 스크립트다. 에이전트가 "내 페이지에 여우 올려줘" 또는 "초콜릿색 수달 그려줘"를 받아 새 캐릭터를 그리고, 두 시트를 정렬해 붙이고, 클릭 시 튀지 않는지 검증한 뒤 컴포넌트까지 얹는다. 세 스크립트의 규율이 특히 볼 만하다.

  • generate.py: 방향 시트를 먼저 만들고, 표정 시트는 그 방향 시트를 참조 이미지로 넘겨 EDITS 엔드포인트로 만든다. 설명을 기억시키는 대신 실제 픽셀을 맞추게 하는 것이다. 저자 스스로 "두 시트를 일치시키는 게 이 파이프라인의 전부이고, 어긋나면 클릭하는 순간 마스코트가 눈에 띄게 튄다"고 적었다.
  • build.py: 두 3×3 원본을 아틀라스로 조립하면서 캐릭터를 알파 바운딩 박스가 아니라 가장 큰 연결 덩어리로 앵커링한다. 예전 버전은 떠다니는 하트·반짝임을 박스에 포함시켜 그 칸에서 캐릭터를 아래로 밀어냈다.
  • verify.py: 클릭으로 아틀라스가 바뀔 때 몸이 실제로 몇 px 움직이는지 잰다. "하체에서 가장 넓은 행" 같은 소박한 랜드마크는 옆선이 곧은 캐릭터에서 같은 폭 두 행 사이를 오가며 안 움직이는 마스코트에 30px 이동을 보고했고, 무게중심은 입만 벌려도 움직였다. 그래서 반응 프레임의 몸 밴드를 방향 중앙 프레임에 밀어 맞춰 가장 잘 겹치는 오프셋을 찾는다.

스프라이트 시트를 칸 경계로만 자르면 캐릭터가 제자리에서 안 도는 문제를, 이 저장소는 세 단계로 나눠 스크립트로 눌렀다. 코드 인용은 짧게 남긴다(MIT, 출처 표기).

해자와 균열

  • 해자: 저자 화제성 ↔ 균열: 화제성은 유지보수를 보장하지 않는다. 하루 5커밋·1인·테스트 0·CI 0이라 지속 여부는 아직 근거가 없다. 관심은 오늘의 것이고, 커밋 이력은 아직 하루치다.
  • 해자: 즉시 쓰는 52종 + 무의존 컴포넌트 ↔ 균열: 컴포넌트 자체는 영리하고 가볍지만, 저장소를 프로덕션 의존성으로 삼기엔 버전 태그도 릴리스도 없다(remoteTagCount 0). 시트 두 장만 복사하면 컴포넌트는 어느 React 프로젝트서든 돌아가므로, 저장소를 의존하기보다 시트를 가져다 쓰는 게 실제 사용 형태다.
  • 해자: 새 캐릭터 자동 생성 ↔ 균열: "그려줘"는 유료 이미지 API(gpt-image-2.5-sunburst)에 묶여 있고, 저자도 인정하듯 두 시트의 일치도는 이미지 모델 세대에 좌우된다. 파이프라인의 규율이 좋아도 생성 품질은 모델 의존이고 비용이 든다.

실제로 안 되는 것

갓 출시라 교차 검증할 핸즈온 리뷰가 아직 없다(공개 이슈 2건, 응답 0). 그래서 이 항목은 마케팅이 아니라 저장소에서 관찰되는 제약으로 채운다. fine pointer가 없으면 트래킹이 꺼지므로 터치 기기에서는 머리가 커서를 따라오지 않는다(설계상 의도). 새 캐릭터 경로는 이미지 API 키·비용·모델 편차를 전제한다. 테스트가 없으므로 새 스타일·새 캐릭터가 정렬을 깨는지는 verify.py의 수치를 사람이 보고 판단해야 한다.

실측 정보

HEAD 커밋
f9c92d7 (2026-09-14, 전체 5커밋)
저자
Kamran Ahmed 단독 (roadmap.sh 창시자)
형태
React 컴포넌트(src/mascot.tsx) + Claude Code 스킬(skills/page-mascot, 파이썬 5스크립트) + 데모 사이트(site/)
규모
텍스트 5,272줄 · tsx 10파일 1,078줄 · py 5파일 1,001줄 · 런타임 의존성 0 / devDep 11
동작 원리
캐릭터 1종 = 두 장의 3×3 스프라이트(9방향 + 9표정). 포인터 각도가 방향 셀을 고르고(데드존), 클릭 시 반응 셀 0.5초. background-position 스왑, 애니메이션 라이브러리 없음
스타일
같은 캐릭터를 colour·ink·sketch·riso·paper·pixel 6종으로 렌더 (렌더만 바뀌어 정렬 유지)
접근성
fine pointer 없으면 트래킹 off · click squash는 prefers-reduced-motion 존중 · 스크린리더 라벨
새 캐릭터 그리기
OpenAI 이미지 API(gpt-image-2.5-sunburst) 또는 Codex 자체 도구·사용자 사진. OPENAI_API_KEY 필요
테스트 / CI
테스트 0 · CI 워크플로우 0
라이선스
MIT © Kamran Ahmed

사이트 실측

주소
koboyo.com/page-mascot
응답 · 첫 화면
200 · 2.4초 (데스크톱)
요청 · 전송량
124개 · 21.0MB
깨진 요청 · 콘솔 오류
0개 · 0개
폰(390px)
가로 넘침 없음
디자인
밝은 화면 · Inter

홈페이지를 실제 크롬으로 띄워 첫 화면까지만 쟀습니다(2026-10-16 · 데스크톱 1440px · 폰 390px). 로그인 · 입력 같은 동작은 하지 않았고, 사이트 상태는 리포 품질과 따로 봅니다. 색 칸은 바탕 · 면 · 글자 · 흐린 글자 · 강조 · 선, 점선은 「못 잼」.

적용점

  • 이럴 때 유용포트폴리오·랜딩에 커서를 따라오는 캐릭터를 붙이고 싶을 때. 기성 캐릭터의 시트 두 장만 복사하면 어느 React 프로젝트서든 런타임 의존성 0으로 돌아간다.
  • 이럴 땐 부적합터치 기기 중심 화면. fine pointer가 없으면 트래킹이 꺼진다(설계상 의도). 새 캐릭터를 그리는 경로는 유료 이미지 API 키와 비용을 전제한다.
  • 먼저 볼 것스프라이트 정렬을 검증하는 verify.py가 이 저장소의 숨은 보석이다. 마스코트·스프라이트를 다루는 사람에겐 컴포넌트보다 이 정렬 파이프라인이 참고감. 단 하루 5커밋·1인·테스트 0이라 프로덕션 의존은 이르다.
◈ 참고용

컴포넌트는 가볍고 영리하며, 스프라이트 정렬을 generate → build → verify로 나눠 검증하는 설계는 이 저장소를 넘어 마스코트·스프라이트를 다루는 누구에게나 교보재다. 다만 하루 5커밋·1인·테스트 0·태그 0이라 프로덕션 의존성으로 채택하기엔 이르다. 시트 두 장을 가져다 컴포넌트를 붙이는 즉시 사용과, 정렬 파이프라인의 기법 참고가 지금의 올바른 사용 형태라 도입이 아니라 참고용으로 둔다.

원문 README

아래는 nilbuild/page-mascot 저장소의 README 원문입니다(GitHub 렌더, 수집 시점 기준). 저작권은 원 프로젝트에 있으며 원 저장소의 라이선스를 따릅니다. GitHub에서 보기

README 전문 펼치기

page-mascot

An interactive character that watches the cursor and blinks when you poke it.

See the fifty-two →

Install

npm i page-mascot

Use existing mascots

Pick a character on the demo page and download its two sheets into public/mascots, then point the component at them:

import { Mascot } from 'page-mascot'

<Mascot
  directions="/mascots/fox-directions.webp"
  reactions="/mascots/fox-reactions.webp"
/>

The paths are whatever your app serves, so imported images work too.

Or let an agent do it, once the skill below is installed:

/page-mascot put the fox on my page

Draw your own

Install the skill:

npx skills add nilbuild/page-mascot --skill page-mascot --global --yes

Then ask your agent:

/page-mascot a chibi otter with chocolate-brown fur
/page-mascot make one that looks like me        [attach a photo]
/page-mascot a chibi fox, in the riso style

It draws the nine directions and nine expressions, builds them into two aligned sheets, checks the character does not jump between them, and puts the component on your page, with the same two props as above.

Drawing needs an image tool. Codex has its own; Claude Code goes through the OpenAI images API:

export OPENAI_API_KEY=sk-...

Or take the prompts and use them in any chat UI.

Props

prop default
directions none path to the directions sheet
reactions none path to the reactions sheet
size 140 px, square
label 'mascot' what a screen reader calls it
className

Tracking switches off without a fine pointer, and the click squash honours prefers-reduced-motion.

How it works

Each character is two 3×3 sprite sheets: nine head directions, and nine expressions.

the two sheets that make up one character

The pointer's angle picks a cell on the directions sheet, with a dead zone so the head settles when the cursor is close. A click shows a cell from the reactions sheet for half a second.

The same character can be drawn in six styles: colour, ink, sketch, riso, paper and pixel. Only the rendering changes, so the alignment holds.

the same fox drawn in six styles

License

MIT © Kamran Ahmed

의견

판정에 대한 반론, 도입 경험, 정정 제보를 환영합니다. GitHub 계정으로 참여합니다.

이것도 재봤습니다

전체 리포트 →