Hoyomi is a modern, open-source app to watch anime and read manga in one seamless experience. Designed to be lightweight, fast, and fully cross-platform, Hoyomi runs smoothly on Android, iOS, Linux, macOS, Windows, and even on the web.
Warning
App is development
Multiple service support. Please check this:
- lib/core_services/comic/services - Service for comic, manga...
- lib/core_services/eiga/services - Service for eiga, film...
3rd party services are now supported with the power of typescript
. Check out the example here hoyomi_bridge_ts/example or hoyomi-plugin-animehay
- 🎥 Stream anime with subtitle support (custom fonts, colors, styles)
- 📚 Read manga from multiple sources in a clean, scrollable reader
- 🌐 Multi-platform: Mobile, desktop & web support out of the box
- 🎨 Highly customizable UI: Dark/light themes, font settings, subtitle styles
- 💬 Offline support: Cache and view anime/manga offline
(development)
- 🧩 Plugin-friendly: Extend functionality with community-built integrations
- 🔐 Privacy first: No tracking, no ads, no data collection
Get the app from our releases page.





If your device runs iOS 14.0 beta 2 – 16.6.1, 16.7 RC (20H18)
, or 17.0
, you’re eligible to be a first-class citizen of TrollStore 🚀.
Install it for free, then enjoy Hoyomi with permanent installation — no resigning, no revokes, forever free.
Hoyomi is fully open-source, actively maintained by the community. We welcome contributions and ideas. You’re not just a user — you’re part of the project.
- ✅ Android
- ✅ iOS
- ✅ Windows (.exe)
- ✅ Linux (.deb, .AppImage)
- ✅ macOS (.dmg)
- ✅ Web (PWA)
Hoyomi does not collect, store, or share your personal data. Your activity stays on your device. See our full Privacy Policy and EULA for details.














- Android
- Isar for android SDK <= 23
- iOS side via TrollStore
- iOS side by other methods
Hoyomi's architecture allows for easy extension through custom plugins written in TypeScript. You can create new comic or eiga services by following these general steps:
- Define your service interface: Create a TypeScript file that defines the methods and data structures for your comic or eiga service, adhering to the
hoyomi_bridge_ts
conventions. - Implement the service logic: Write the actual logic for fetching data, parsing content, and handling interactions within your TypeScript service.
- Register your plugin: Use the
hoyomi_bridge_ts
to register your implemented service, making it available to the Hoyomi application.
Refer to the existing examples for detailed implementation patterns:
Here's a simplified example of an Eiga plugin:
// example_eiga_plugin.ts
import {
ABEigaService,
createOImage,
registerPlugin,
StatusEnum,
type EigaCategory,
type EigaEpisode,
type EigaEpisodes,
type EigaHome,
type MetaEiga,
type ServerSource,
type ServiceInit,
type SourceVideo
} from "hoyomi_bridge_ts";
class MyCustomEigaService extends ABEigaService {
override init: ServiceInit = {
name: "My Custom Eiga",
faviconUrl: createOImage("https://example.com/favicon.ico"),
rootUrl: "https://example.com"
};
async getURL(eigaId: string, chapterId?: string): Promise<string> {
// Logic to get the URL for a specific eiga or episode
return `https://example.com/eiga/${eigaId}`;
}
async home(): Promise<EigaHome> {
// Logic to fetch home page data (categories, popular eiga, etc.)
return {
categories: [
{
name: "Popular Eiga",
items: [
{
name: "Example Eiga 1",
eigaId: "example-1",
image: createOImage("https://example.com/image1.jpg")
}
]
}
]
};
}
async getCategory(params: {
categoryId: string;
page: number;
filters: { [key: string]: string[] | null };
}): Promise<EigaCategory> {
// Logic to fetch eiga within a specific category
return {
name: "Category Name",
url: "",
items: [],
page: params.page,
totalItems: 0,
totalPages: 0
};
}
async getDetails(eigaId: string): Promise<MetaEiga> {
// Logic to fetch detailed information about an eiga
return {
name: "Example Eiga Details",
image: createOImage("https://example.com/details.jpg"),
status: StatusEnum.Ongoing,
genres: ["Action", "Adventure"],
description: "A brief description of the eiga.",
seasons: []
};
}
async getEpisodes(eigaId: string): Promise<EigaEpisodes> {
// Logic to fetch episodes for an eiga
return {
episodes: [
{
name: "Episode 1",
episodeId: "ep-1"
}
]
};
}
async getSource(params: {
eigaId: string;
episode: EigaEpisode;
server?: ServerSource;
}): Promise<SourceVideo> {
// Logic to get video source for an episode
return {
src: "https://example.com/video.mp4",
url: "https://example.com/video.mp4",
type: "video/mp4"
};
}
async search(params: {
keyword: string;
page: number;
filters: { [key: string]: string[] | null };
quick: boolean;
}): Promise<EigaCategory> {
// Logic to search for eiga
return {
name: `Search results for "${params.keyword}"`,
url: "",
items: [],
page: params.page,
totalItems: 0,
totalPages: 0
};
}
}
registerPlugin(MyCustomEigaService);
Here's a simplified example of a Comic plugin:
// example_comic_plugin.ts
import {
ABComicService,
ComicModes,
createOImage,
registerPlugin,
StatusEnum,
type ComicCategory,
type ComicHome,
type MetaComic,
type OImage,
type ServiceInit
} from "hoyomi_bridge_ts";
class MyCustomComicService extends ABComicService {
override init: ServiceInit = {
name: "My Custom Comic",
faviconUrl: createOImage("https://example.com/comic-favicon.ico"),
rootUrl: "https://example.com/comic"
};
async getURL(comicId: string, chapterId?: string): Promise<string> {
// Logic to get the URL for a specific comic or chapter
return `https://example.com/comic/${comicId}`;
}
async home(): Promise<ComicHome> {
// Logic to fetch home page data (categories, popular comics, etc.)
return {
categories: [
{
name: "Popular Comics",
items: [
{
name: "Example Comic 1",
comicId: "comic-1",
image: createOImage("https://example.com/comic1.jpg")
}
]
}
]
};
}
async getCategory(params: {
categoryId: string;
page: number;
filters: { [key: string]: string[] | null };
}): Promise<ComicCategory> {
// Logic to fetch comics within a specific category
return {
name: "Comic Category Name",
url: "",
items: [],
page: params.page,
totalItems: 0,
totalPages: 0
};
}
async getDetails(comicId: string): Promise<MetaComic> {
// Logic to fetch detailed information about a comic
return {
name: "Example Comic Details",
image: createOImage("https://example.com/comic-details.jpg"),
status: StatusEnum.Ongoing,
genres: ["Fantasy", "Adventure"],
description: "A brief description of the comic.",
chapters: [
{
name: "Chapter 1",
chapterId: "ch-1",
time: new Date(),
order: 1,
}
],
lastModified: new Date()
};
}
async getPages(comicId: string, chapterId: string): Promise<OImage[]> {
// Logic to get image pages for a specific chapter
return [
createOImage("https://example.com/comic/page1.jpg"),
createOImage("https://example.com/comic/page2.jpg"),
];
}
async search(params: {
keyword: string;
page: number;
filters: { [key: string]: string[] | null };
quick: boolean;
}): Promise<ComicCategory> {
// Logic to search for comics
return {
name: `Search results for "${params.keyword}"`,
url: "",
items: [],
page: params.page,
totalItems: 0,
totalPages: 0
};
}
getComicModes(comic: MetaComic): ComicModes {
// Define how the comic should be read (e.g., leftToRight, rightToLeft, webtoon)
return ComicModes.leftToRight;
}
}
registerPlugin(MyCustomComicService);
-
Add background image for
details_comic
-
Add information
book
for reader -
Fix logic fake page
-
Page eiga details
-
Fix zoomer read manga
-
Responsive for video player
-
AppBar all page
-
API comment for eiga
-
API follow anime
-
API notification
-
A11y manga reader
-
API playlist
-
API playlist online
-
Search icon for all section
-
Bottom sheet show all options
-
Add multiple server in eiga
The first step is to set up the Firebase project and enable Google sign-in. if you already have a flutter project, you can skip this step.
- Go to the Firebase console and create a new project.
- Click on the
Authentication
link in the left-hand menu, then click on theSign-in Method
tab. - Enable the
Google
sign-in method.
You need your application's client ID and Secret from Google Cloud Console to enable Google sign-in. If you’ve it already then skip this step.
- To get the client ID and secret, follow the steps from the given link.
- Choose
a web application
. - In the
Authorized redirect URIs
andAuthorized JavaScript origin
, enter the URLhttp://localhost
To deploy the serverless application, you need to set up a serverless provider. Here are the steps to set up Deno:
- Click to
Generate new private key
- Paste file download to
serverless/service-account-key.json
The server required database for working
- Run
cd serverless
- Add
DATABASE_URL
fromsetting project
to.env
Tip
This project depends Firebase. Please first run
flutterfire configure
and configuring file /android/app/google-services.json
, /ios/Runner/GoogleService-Info.plist
(two file auto create by flutterfire
)
/android/app/google-services.json
required for Android/ios/Runner/GoogleService-Info.plist
required for iOS
Tip
NOTE (If you development for iOS)
Please edit CFBundleURLSchemes
in ios/Runner/Info.plist
Goto https://console.cloud.google.com/apis/credentials and get Client ID
and Client secret
from OAuth 2.0 Client IDs
(Use Web application
)
Set to .env
GOOGLE_CLIENT_ID=<Client ID>
GOOGLE_CLIENT_SECRET=<Client secret>
Set to .env
BASE_API_GENERAL=<URL base API general serverless>
To release the application, the following secrets
must be provided:
ENV_CONTENT
- Content of the.env
file. (not encode base64)
KEYSTORE_CONTENT
- Base64-encoded content of thekeystore.jks
file.KEYSTORE_PASSWORD
- Password used to sign thekeystore.jks
file.KEYSTORE_ALIAS
- Alias used to sign thekeystore.jks
file.GOOGLE_SERVICES_JSON
- Base64-encoded content of thegoogle-services.json
file (automatically generated byflutterfire
).
GOOGLE_SERVICE_INFO_PLIST
- Base64-encoded content of theGoogleService-Info.plist
file (automatically generated byflutterfire
).
Note: The
google-services.json
(for Android) andGoogleService-Info.plist
(for iOS) files are automatically created when you run theflutterfire configure
command during Firebase setup.