Open-source project
tianxingleo/ACGTI avatar
tianxingleo/ACGTI

ACGTI: an MBTI-style quiz that maps anime characters, with the scoring in the open

ACG Type Indicator - MBTI inspired anime persona quiz

1,130 stars78 forksTypeScriptApache-2.0

At a glance

What is it?
ACGTI is a Vue 3 and TypeScript quiz site that scores 39 seven-point questions across the four MBTI dimensions, matches the result to one of eight anime archetypes and then to a single character out of a library of more than 110. The whole scoring engine, the calibration pipeline and the privacy trade-offs are described in the repository.
Who is it for?
ACGTI suits a developer who wants to read a working personality-quiz engine, with its scoring weights, its calibration pipeline and its analytics in the open, and a reader who wants an anime character rather than a letter code. It is explicitly a game, and the README says it cannot replace psychological diagnosis or medical assessment, so treat the result as entertainment.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 72 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Four dimensions, eight archetypes, one character

ACGTI is the ACG Type Indicator, a quiz site that takes MBTI as its starting point and lands on an anime character instead of a four-letter code. The live site is at acgti.tianxingleo.top, and the project is licensed Apache-2.0 with the primary language TypeScript.

The design is a three-stage mapping. The four MBTI dimensions, E/I, S/N, T/F and J/P, are scored first. Those four results are then mapped to one of eight anime archetypes, which the project describes with names for each. Finally the result is matched against a character library of more than 110 characters drawn from over 60 works, including BanG Dream, Bocchi the Rock, Wuthering Waves, Arknights, K-On, Oshi no Ko, Re:Zero, Genshin Impact, Honkai: Star Rail, Frieren and Fate/stay night. The output is a single character and a custom character code.

The quiz itself is 39 situational questions, each on a seven-level scale running from minus three to plus three, and each question carries an associated MBTI dimension plus archetype weights.

The character library is not hand-written either. The source data lives in `src/content/characters/` with one JSON file per character holding metadata, visuals and localisation, and the aggregated files under `src/data/` such as `characters.json` and `characterVisuals.json` are marked as automatically generated, with a script that builds them.

The README is careful about framing. There is a warning that the tool is for entertainment only, that it cannot replace psychological diagnosis or medical assessment, and that it should not be used to draw conclusions about real people.

Four weighted layers, and why they are visible

The scoring is where the interesting engineering is, and the README publishes the weights.

The pipeline runs: answer the 39 seven-level questions, score with dimension weights and archetype weights, match one of the eight archetypes, hit a character, then show the result. The scoring itself combines four layers. The MBTI dimension weights account for 25 percent, archetype weights 28 percent, character vectors 27 percent and character-specific weights 20 percent. The output is dimension tendency percentages.

The character match is a softmax over the character library, comparing the dimension result against each character's six-dimension vector, and it hits one primary character and produces a custom character code for it.

Why the weights are visible is the interesting part. The project collects self-reported real MBTI from users and treats those as labels, replaying them offline over historical answer data with per-question and per-dimension switches to compare weight configurations and their match accuracy and dimension bias, then shipping the verified best configuration. Each calibration round carries its own version number and can be rolled back with one click.

That is a proper evaluation loop for a scoring function, and it is unusual to see it documented in a hobby project. The per-question weight file `questionDimensionWeights.json` is described as a calibration product, and the `analysis/` directory holds the feedback analysis and weight calibration pipeline, which the README notes runs locally.

A static frontend on Cloudflare Pages with D1 behind it

The stack is Vue 3 with TypeScript and Vite on the front, deployed to Cloudflare Pages, with Cloudflare D1 as the database. The manifest pins Vue 3.5, vue-router 4.6 and Vite 8, with `vue-tsc` for type checking and Vitest for tests, and the README shows a Node 22 or later requirement.

The Pages Functions directory is where the server side lives, and the split of files tells you what the backend actually does. There is a global middleware handling cross-site validation on write endpoints, security response headers and tracking parameter cleaning on the home page. The API endpoints are a health check ping, an anonymous submit endpoint that does aggregate increments plus sampled detail, a feedback endpoint for the self-reported MBTI, an insight endpoint for the AI interpretation with multi-level fallback and D1 bucket caching, and a stats directory with queries for overview, archetypes, characters and result.

There is a separate Cron Worker for recomputing statistics snapshots and cleaning up the rate limit tables, and a `migrations/` directory for the D1 schema, with the note that CI dry-runs the migrations against a fresh database.

The build script itself is worth reading, because it shows the order of operations:

json
"build": "node scripts/validate-character-data.mjs && node scripts/build-character-data.mjs && vue-tsc -b && vite build"

Data validation runs first, then the character data build, then the type check, and only then the Vite build. A character data problem fails the build rather than shipping.

What leaves the browser and what does not

This is the part of the README that is most unusual, and the part most worth reading before you fork it.

The claim is that the test is computed in your browser, that no registration is required, and that no identity information such as an email address is collected. On submission, the final matched character and archetype are reported anonymously for site-wide statistics and a leaderboard.

Then there is the sentence that makes the privacy story more nuanced. About 2 percent of submissions are sampled to retain anonymised per-answer data, used to calibrate the questions, with the details in the SECURITY.md file. So the answer-level data is not collected in bulk, but a sample is kept, and that is exactly the kind of thing a privacy policy is supposed to say plainly.

The AI interpretation feature is designed around the same constraint. The insight request carries only the tendency buckets, not the per-question answers. It is built on Cloudflare Workers AI with what the README calls zero-key deployment, and when the quota runs out the card is hidden and the rest of the site is unaffected.

The .env.example file shows how many optional channels that feature has. The insight endpoint has a primary OpenAI-compatible gateway, a second gateway with an independent concurrent quota used only by the prewarm script, and a Cloudflare Workers AI REST fallback. All of them are optional, and if none is configured the endpoint returns a no-binding degraded response and the frontend hides the card.

The environment variables, and the Turnstile gate

The .env.example file is a good map of what the deployment needs, and it is mostly optional.

For advertising there are three Vite-prefixed variables: the AdSense client, a home slot and a result slot. The code has a `utils/adsense.ts` module and an `AdsenseSlot.vue` component, so the ad slots are part of the design rather than an afterthought, and you can see them in the screenshot of the result page.

For bot protection there are Turnstile keys. The site key is a Vite-prefixed frontend variable, with a legacy server-side fallback read by `functions/api/config.ts` for local wrangler work, and a separate secret key used by the Pages Functions for validation. The config endpoint's stated job is runtime configuration, and specifically delivering the Turnstile site key to the client, so the site key does not have to be hardcoded into the bundle.

The cron trigger has its own bearer secret, and the comment is explicit that manual triggering requires it and that an unconfigured trigger returns 403 outright.

Then there is the AI configuration, with the variables grouped by channel. The two gateway channels need three values each before they count as configured, and the comment is pointed about it: the code sets no default endpoint and no default model, so the gateway address has to be given explicitly. There is also an AI token and account id for the Workers AI REST path, a per-minute insight rate limit override that defaults to 10 per minute per IP and is only relaxed for local prewarming, a prewarm token for the privileged prewarm path, and a daily limit on real AI generation across the whole site, defaulting to 1000 and counting only unauthenticated requests.

The pattern is consistent: everything optional is off unless configured, and the limits are conservative by default.

Tests, scripts and the pieces a fork has to keep in sync

The scripts in the manifest show the maintenance surface of a project with generated data and several backends.

There are separate commands for validating character data and for generating it, and a prewarm script for the AI insight cache, a script to generate the Open Graph image, a Pages dev script that reuses the local wrangler OAuth login state to inject the Workers AI token automatically, and an end-to-end smoke test script that runs through Puppeteer.

The test suite is Vitest, and the directory listing names what it covers: the scoring engine, colour utilities, storage, the quiz state machine, and the backend endpoints and middleware. There is a `vitest.config.ts` at the root and separate tsconfig files for the app, the functions, the node side, and the tests, which is what a codebase with a frontend and a Workers backend in one repository needs.

The `src/` layout is worth noting for a fork. Composables hold the stateful logic: `useQuiz.ts` for test state, progress persistence and the entry point to result calculation, `useCharacterRarity.ts` for rarity badges shared between the result page and the poster, `useShare.ts` for share text and poster export, and `useSeo.ts` for meta tags, Open Graph and JSON-LD. The utils directory holds the scoring engine, rarity bucketing, character probability, the anonymous stats reporter, the AI insight client, and localStorage helpers. The i18n directory covers Simplified Chinese, Traditional Chinese, English and Japanese, with character copy loaded on demand.

One thing to plan for is that several data files are generated. `characterMessages.json` and `characterVisuals.json` are produced by the build script, so editing them by hand will be overwritten, and the source of truth is one JSON file per character under `src/content/characters/`.

Editorial conclusion

ACGTI suits a developer who wants to read a working personality-quiz engine, with its scoring weights, its calibration pipeline and its analytics in the open, and a reader who wants an anime character rather than a letter code. It is explicitly a game, and the README says it cannot replace psychological diagnosis or medical assessment, so treat the result as entertainment. Before deploying a fork, read the SECURITY.md file on the roughly 2 percent sampled per-answer telemetry, and the environment variable list in .env.example, since the AI interpretation feature is entirely optional and every one of its channels is disabled unless configured.

Frequently asked questions

What is ACGTI?

It is the ACG Type Indicator, an open source quiz site that takes MBTI as its basis and maps the result onto an anime character. The four MBTI dimensions are scored first, then mapped to one of eight archetypes, then matched to a single character from a library of more than 110 characters across over 60 works.

How does ACGTI score the quiz?

39 questions on a seven-level scale from minus three to plus three are combined across four weighted layers: MBTI dimension weights at 25 percent, archetype weights at 28 percent, character vectors at 27 percent and character-specific weights at 20 percent. The character match is a softmax over the library, hitting one primary character and a custom character code.

Does ACGTI collect my answers?

The test is computed in your browser with no registration and no email, and the final character and archetype are reported anonymously for statistics. About 2 percent of submissions are sampled to retain anonymised per-answer data for question calibration, which is documented in SECURITY.md.

Is the AI result interpretation required?

No. It is built on Cloudflare Workers AI with a gateway, a second gateway and a REST fallback, and every channel is optional. If none is configured the endpoint returns a no-binding degraded response and the frontend hides the interpretation card without affecting other features.

How does ACGTI improve its accuracy?

Users can voluntarily submit their real MBTI, and those are treated as labels replayed offline over historical answer data with per-question and per-dimension switches, so different weight configurations can be compared on match accuracy and dimension bias before the best one is shipped. Each calibration round has its own version number and can be rolled back.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. tianxingleo/ACGTI on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tianxingleo-acgti.svg)](https://hysenlabs.com/projects/tianxingleo-acgti)

Was this useful?