Self-hosted service
besscroft/PicImpact avatar
besscroft/PicImpact

PicImpact: a self-hosted photography portfolio with a WebGL viewer and Postgres behind it

PicImpact: a self-hosted photography portfolio website with a range of features. Share yourself and the world.

1,308 stars211 forksTypeScriptMIT

At a glance

What is it?
PicImpact is a Next.js plus Hono.js gallery you deploy yourself, storing images on S3-compatible storage while PostgreSQL holds the album, tag and EXIF metadata. The interesting parts are the WebGL viewer and the metadata model; the cost is a database, object storage and a set of auth secrets you now own.
Who is it for?
Adopt PicImpact if you already run PostgreSQL and an S3-compatible bucket, want EXIF-driven map views and tag filtering without writing a gallery yourself, and are willing to keep the database and the BETTER_AUTH_* secrets under your own control. Do not adopt it if you want a single static folder of HTML you can drop on any host, or if you need storage backends beyond S3 API, Cloudflare R2 and Open List API.
Can I use it commercially?
Yes. MIT 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 17 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 October 8, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem PicImpact solves: a portfolio site that owns its own metadata

Most photographers who want a portfolio either hand a monthly fee to a hosted service or assemble something themselves from a static site generator and a folder of JPEGs. The hosted route hides the EXIF data behind someone else's interface. The static route gives you a page of images and nothing else: no tags, no map, no search, no admin panel. PicImpact sits between those two. It is a self-deployable photography site, built with Next.js and Hono.js, that keeps a real database of your images rather than a directory listing. That database is what makes tag filtering, geotagged map markers and per-image EXIF pages possible. The audience is narrow and specific: someone comfortable running PostgreSQL and an object storage bucket, who wants the gallery to be a real application with an admin backend, and who cares about Live Photos and EXIF coordinates. If you only want to publish twelve images a year, the operational surface here is larger than the problem.

What actually runs: Next.js, Hono.js, Prisma and a WebGL viewer

The stack table in the README is the clearest statement of the architecture. Next.js handles rendering, and the project describes SSR-based hybrid rendering with a state mechanism. Hono.js sits alongside it, which in a Next.js project usually means the API surface is handled by a separate small framework rather than by Next.js route handlers. PostgreSQL is the database, accessed through Prisma, and the README states that Prisma handles automatic database initialization and data migration, which is the part that most self-hosted projects make you do by hand. Authentication is delegated to better-auth, covering email and password, TOTP and WebAuthn. Image rendering is a WebGL viewer based on Afilmory, and the README describes tiling and LOD as the mechanism for large images. That combination tells you where the load sits: the browser does the zooming and panning work on the GPU, the server does not generate resized derivatives on the fly for viewing, and the database holds the relationships. The upload path is the exception. According to the README, batch upload generates a 0.3x compressed image at upload time, so the derivative is produced once and stored, not computed per request. That is a deliberate choice with a cost: it is fast to serve and it doubles the objects in your bucket.

Deployment paths and the environment keys you must set

There are three deployment shapes in the material: Vercel, Node.js, and Docker or Kubernetes. The Vercel path is the one with a button, and it comes with a warning in bold: set the Build Command to pnpm run build:vercel. For a Node.js deployment the README names pnpm run build:node instead. Local development is three commands: git clone the repository, pnpm i, then pnpm run dev. The environment variables are where the real configuration lives. DATABASE_URL is the PostgreSQL connection string, and the README gives a Supabase example using port 6543 with pgbouncer=true. DIRECT_URL exists separately because prisma migrate needs a direct connection; for a non-serverless database the README says it can be identical to DATABASE_URL. BETTER_AUTH_SECRET is generated with npx auth secret. BETTER_AUTH_URL is your public address. BETTER_AUTH_PASSKEY_RP_ID is your domain, and BETTER_AUTH_PASSKEY_RP_NAME is a display name such as PicImpact. One constraint is stated plainly: Passkey depends on an HTTPS environment, with localhost allowed for local development. If you terminate TLS somewhere that does not present the right origin to the browser, WebAuthn will not work, and the README does not describe a workaround.

Storage backends and the metadata that only exists if uploads go through the app

PicImpact lists three storage options: S3 API, Cloudflare R2, and Open List API. That is a compatibility claim, not a description of behaviour. The README does not say whether all three support every feature, and it does not describe how credentials are configured per backend beyond the existence of a storage configuration screen in the admin dashboard. Treat that as the first thing to verify on your own infrastructure. The metadata story has a similar shape. EXIF information, map coordinates and tags are all presented as per-image data, which means they must be captured when the image enters the system. The README describes batch automated upload as the path that generates the 0.3x preview, so images added by that route get both the derivative and, presumably, the metadata extraction. Images that arrive some other way, or that are moved into the bucket directly, are not covered by any statement in the material. If you point PicImpact at a bucket that already contains years of photos, you should not assume the map view and EXIF pages will populate. The README does not document an import or backfill command.

Authentication is heavier than most galleries need

The auth feature list is longer than the gallery feature list in places. Email and password, TOTP per RFC 6238 with Google Authenticator, Microsoft Authenticator and 1Password named as compatible clients, and Passkey via WebAuthn with biometrics and hardware keys. This is better-auth doing the work, and it is a genuine advantage over the typical self-hosted gallery that ships a single admin password in an environment variable. It is also a configuration burden. You are now responsible for BETTER_AUTH_SECRET, for the relying party ID matching your domain, and for HTTPS being present, because Passkey will not function without it. The failure mode is quiet: passkeys simply will not register, and you fall back to a password without understanding why. There is no statement in the README about what happens to existing sessions when BETTER_AUTH_SECRET changes, so rotate it deliberately and expect to re-authenticate.

Where PicImpact is the wrong tool

The clearest case against it is a static gallery. If your images are already processed, you do not need per-image database rows, and you do not want to run PostgreSQL to display them. A static site generator that reads EXIF at build time will give you a folder of HTML you can host anywhere, with no secrets and no migration step. The second case is a shared or multi-user photo platform. PicImpact is described as a photography portfolio, with an admin dashboard for statistics, upload, image management, album management, settings and storage configuration. That is a single-operator model. Nothing in the README describes per-user accounts, quotas, or upload permissions for anyone other than the administrator. The third case is storage you cannot move. If your images live in a backend outside S3 API, Cloudflare R2 and Open List API, the README offers no adapter path. Finally, the documentation itself is a constraint. The homepage points to pic-docs.besscroft.com, but the English README points to pic-docs.ziyume.com, and the two do not match. That inconsistency is worth resolving before you plan a deployment around either URL.

Release cadence and what it costs you to stay current

The release list shows v3.3.5, v3.3.6 and v3.3.7 within days of each other in June 2026, and the last push to the repository is dated 2026-07-03. The README's own advice is conservative: it recommends updating only when a new version is released. That is a mild contradiction with the cadence, since three patch releases in three days means the release event is frequent. The practical cost is migration. Prisma handles database initialization and migration automatically, which lowers the effort, but automatic migration also means an upgrade can alter your schema without you reviewing the SQL first. If that matters to you, run the migration against a copy of the database before pointing it at production. The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are preserved. That is the standard permissive arrangement and it does not obligate you to publish your own changes. This is a description of the licence text, not legal advice; if you are embedding PicImpact in a product, read the LICENSE file in the repository yourself.

Editorial conclusion

Adopt PicImpact if you already run PostgreSQL and an S3-compatible bucket, want EXIF-driven map views and tag filtering without writing a gallery yourself, and are willing to keep the database and the BETTER_AUTH_* secrets under your own control. Do not adopt it if you want a single static folder of HTML you can drop on any host, or if you need storage backends beyond S3 API, Cloudflare R2 and Open List API. Before committing, verify three things against your own setup: that DIRECT_URL reaches your PostgreSQL instance for prisma migrate, that the domain you put in BETTER_AUTH_PASSKEY_RP_ID is served over HTTPS, and that your chosen storage backend can serve full-resolution originals for direct link access, since the README does not state which of the three backends support which operations.

Frequently asked questions

What stack does PicImpact need in order to run?

It is a Next.js application paired with a Hono.js server, PostgreSQL through Prisma, and Radix plus shadcn/ui on the front end. package.json requires Node.js >=24.0.0 and pins [email protected], and the container base image is node:24-alpine3.23. Deployment is supported on Vercel, a Node.js host, a Docker container, and Kubernetes.

Does PicImpact support two-factor authentication and passkey login?

Both are built in. Two-factor uses TOTP under RFC 6238, with Google Authenticator, Microsoft Authenticator and 1Password named as clients, and passkey login follows the WebAuthn standard for biometrics and hardware keys. Passkeys need an HTTPS environment, with localhost accepted during local development, and they depend on BETTER_AUTH_PASSKEY_RP_ID and BETTER_AUTH_PASSKEY_RP_NAME.

Where can PicImpact keep the image files?

The storage layer speaks the S3 API, and Cloudflare R2 and Open List API are named as compatible targets. The dependency list pins @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner, and the admin dashboard carries a storage configuration panel next to statistics, upload, image management, album management and settings.

What happens to a photo when it is uploaded to PicImpact?

Batch upload is supported and each upload generates a 0.3x compressed preview for loading. The preprocessing behind that runs from a background ticker, or from an external cron calling POST /api/v1/preprocess-tasks/tick when more than one replica is running, and there is a backfill script at scripts/preprocess-backfill.ts wired to the preprocess:backfill entry.

How do I run PicImpact on my own machine?

The README gives three commands: clone the repository, run pnpm i, then pnpm run dev. The scripts block defines no dev script. The nearest are dev:server as plain next dev, and dev:turbopack, which runs the Prisma dev migration, the generate step and the seed before starting the dev server with --turbopack. You also need a PostgreSQL URL, since .env.example points both DATABASE_URL and DIRECT_URL at a local postgres account.

Official sources

  1. besscroft/PicImpact on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
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/besscroft-picimpact.svg)](https://hysenlabs.com/projects/besscroft-picimpact)