Open-source project
kyechan99/capsule-render avatar
kyechan99/capsule-render

capsule-render turns a query string into a decorative banner image

🌈 Dynamic Coloful Image Render

1,847 stars741 forksTypeScriptMIT

At a glance

What is it?
capsule-render is a hosted image service that draws banner graphics from URL parameters, so a README or a profile page can show a waving, sliced or gradient capsule without anyone opening a design tool. The service is explicitly best effort, and the project tells you to fork and deploy your own copy if the images matter to you.
Who is it for?
capsule-render suits a developer who wants a decorated README or profile banner without designing an image, and it suits anyone who wants to host their own instance of a small image service.
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 2 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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An image as a URL parameter

capsule-render is a service that renders decorative images from query parameters, and the whole interface is one endpoint. The usage section shows the base URL and then the instruction to write the query parameters at the end of it, and the result is embedded like any other image:

code
![header](https://capsule-render.vercel.app/api?type=wave&color=auto&height=300&section=header&text=capsule%20render&fontSize=90)

The same URL works in an HTML img tag, which matters because the target use case is a GitHub README or a profile page, and those are authored in Markdown.

The reason this exists at all is the profile README. The repository topics are dynamic profile, GitHub profile, image generator, profile readme and readme generator, and the service is described in the readme as something that decorates a GitHub profile or any repository. A GitHub profile README is a Markdown file in a repository named after the user, so it can display an image, and a service that renders that image from a URL means the decoration can be generated rather than designed and re-uploaded each time you want a change.

That also explains the parameter set. Everything is a query string, so a banner is reproducible from its own URL, shareable, and can be regenerated by anyone with the same link. The site also hosts a simple Generator, so you can build a URL by clicking rather than by remembering parameter names, and the readme recommends reading the documentation for more detailed adjustments.

The implementation is a TypeScript project deployed as a serverless function, with the manifest entry point at api/index.ts and a dependency on xmldom, which is what you would expect for code that produces SVG markup.

Fifteen shapes, one parameter

The type parameter chooses the background image, and the list is the first thing to read.

The available types are wave, which is the default, egg, shark, slice, rect, soft, rounded, cylinder, waving, venom, speech, blur, pulse, checkered and transparent. The syntax is to write &type= followed by the name on the URL, for example:

code
![header](https://capsule-render.vercel.app/api?type=slice)

The shapes cover three different aesthetics. There are the flat geometric ones, rect, slice, soft, rounded and cylinder, which are the sort of shape you would see in a modern README banner. There are the illustrated ones, wave, waving, egg, shark, venom, speech, blur, pulse and checkered, which are recognisable drawings. And there is transparent, which is what you want when you only want the text and no background.

The distinction between wave and waving, both of which are in the list, is not explained in the readme, and that is a fair example of the kind of small gap that the demonstration gallery exists to fill.

The section parameter then flips the result, reversing the background image, with header as the default and footer as the alternative. The reversal parameter reverses the image left and right, and the readme notes that the colour is reversed at the same time, so reversal and section are two different axes of mirroring and it is worth checking which one you actually want.

Colour: proven random, time based, or exact

The colour parameter has more modes than most people expect, and the distinction between them matters if you want a banner that looks the same every time you reload it.

color=auto gives a proven random colour, meaning the result is drawn from a fixed list, which is in src/pallete.json in the repository. color=timeAuto is also proven random but is decided by the time, so the colour changes with the time of day. color=random is described as really random, with the honest comment that the author does not know what colours are showing, which is a good way of saying it is not reproducible. color=gradient gives a proven random gradient drawn from src/gradient.json, and color=timeGradient is the time based equivalent.

Then there are the exact values. A hex code sets a solid colour, with the default being #B897FF, and the readme warns that if you use a static colour you should not write the hash symbol. A custom gradient is written as a percent-to-colour mapping, for example &color=0:EEFF00,100:a82da8, which is converted into a map of 0 percent to EEFF00 and 100 percent to a82da8.

Two details that save a debugging session. If you use the auto mode you do not need to change the font colour, because auto also changes the font colour automatically, so setting a font colour in that case fights the parameter. And the time based modes are meant for when you use the header and the footer section at the same time, which is the case where a time based colour keeps the two ends of a page coherent.

The customColorList parameter narrows the auto and gradient modes to indices from the palette or gradient file. You pick the index values you want and write them as a comma separated list, and you may repeat an index to make it appear more often, for example &customColorList=0,2,2,5,30. The two JSON files in the source are the reference for what those indices mean.

Themes, and the priority order that decides everything

The theme parameter applies a predefined combination of settings, and the readme is explicit about its precedence: even if color and fontColor are used, the theme has the highest priority.

That is a small sentence with a large consequence. A theme therefore overrides not only the background but the text colours as well, which means a carefully chosen custom gradient and a matching font colour can both be discarded by a theme parameter. If you are mixing a theme with custom settings, the custom settings are the ones that lose.

The available themes are listed in src/pallete_theme.json in the repository, and a themed banner is used like any other:

code
![reversal](https://capsule-render.vercel.app/api?type=rect&text=RECT&fontAlign=30&fontSize=30&desc=Use%20theme&descAlign=60&descAlignY=50&theme=radical)

The theme catalogue is a work in progress. The readme notes that combinations are being added little by little from the theme list of the github-readme-stats project, which tells you where the palettes come from and, more usefully, that the set you can choose from today is smaller than the set you might expect from that source.

For a user, the practical guidance is simple. Pick a theme if you want a known look and change nothing else. Pick colours and fonts if you want control, and leave the theme parameter out. The two approaches are not composable in a meaningful way, and knowing that up front saves the confusion of a banner that ignores your colour choice.

The long tail of text and geometry parameters

The navigation of the readme is a list of the parameters, and it is long enough to be worth summarising by what they control rather than item by item.

The text parameters cover the main title and the description, with the main text, the description, a text background, and a text animation. The font parameters cover the font colour, the font family, the font size, the horizontal and vertical alignment of the title, the size of the description, and the horizontal and vertical alignment of the description. The geometry parameters cover the height of the whole banner, a rotation, a stroke and a stroke width, which together draw an outline around the rendered content.

Two of these deserve a mention. The font family parameter is the one that determines whether a banner looks like a template or like something you designed, since the default is presumably a generic sans serif, and the stroke and stroke width are what make light text readable against a busy background, which is the usual reason people reach for them.

The alignment parameters take percentages, and the readme's own example shows the pattern: fontAlign, fontAlignY, descAlign and descAlignY are all set alongside the text and the font size. The example in the header of the readme combines type, color, height, section, text, fontSize, animation, fontAlignY, desc, descAlignY and descAlign, which is a good illustration of what a full featured URL looks like.

The full parameter list is in the readme, and the Generator on the site produces these URLs for you, which is the sane way to discover them rather than reading a list of names.

The hosted service is best effort, and the answer is to fork it

The most important paragraph in the readme is the important note near the top. This service is provided on a best effort basis and may be unstable due to usage limits or traffic spikes. For reliable use, the project asks you to fork the repository and deploy it to your own Vercel instance, or to another hosting platform.

That is an unusually clear statement for a free service, and it changes how you should use the project. A profile banner that occasionally fails to load is a cosmetic problem. The same service embedded in a README that other people rely on, or in a documentation site, is a dependency you do not control, with an availability you cannot predict and a rate limit you cannot see.

The project is set up to be forked. The API is a single serverless function under api/, the repository is TypeScript with a Vite build for the front end, and the deployment target is documented, and the fork link and the Vercel new project link are both in the note. So the migration path is a fork, a deploy and a change to the hostname in your Markdown.

What you inherit in that fork is visible in the repository layout. There is an api/ directory for the serverless function, a src/ directory for the rendering code, a model/ directory, a types/ directory, a utils/ directory and a constants/ directory, with a __test__/ directory for the Jest tests and a docs/ directory holding the translated readmes. The constants directory and the palette and gradient JSON files are where the visual identity lives, and the theme file is the one you would edit to add your own combinations.

Snapshot tests, translations and a busy commit culture

The engineering setup is the last thing worth describing, because it says how the project expects to change.

The test suite is Jest, with a jest.config.js and a __test__/ directory, and there is a script called test:update that runs Jest with the snapshot update flag. For a service that renders images, that almost certainly means snapshot tests of the rendered output, which is the only practical way to test an image generator without a human looking at every change. The scripts also include a typecheck that runs the TypeScript compiler over the app project, a build that compiles and then runs Vite, a dev script, a preview script, and a start script that runs vercel dev for local serverless work.

The commit workflow is conventional and enforced. There is a commitlint.config.js with the conventional config, a .husky/ directory for git hooks wired up by a prepare script, a .prettierrc and a lint-staged configuration that runs prettier over JavaScript, TypeScript, JSON and CSS files. There is a renovate.json, which is dependency update automation, and a babel.config.js with the TypeScript and React presets for the Jest run, since the tests run through ts-jest and babel-jest.

The front end uses React 19 and the bmates-ui component library, with xmldom in the runtime dependencies, which is consistent with the service building XML or SVG markup to draw the banners. The site itself is a Vite application with a vite.config.ts, an index.html, and separate tsconfig files for the app and the project.

The documentation is unusually thorough for a project of this size. Besides the English readme, there are translated versions in Korean, Spanish, French, German, Simplified Chinese, Traditional Chinese, Traditional Cantonese, Brazilian Portuguese, Japanese, Malayalam and Russian, each in its own file under docs/. The licence is MIT, the repository is not archived, and the last push was on 2026-10-16, so the work is current even though the project publishes no GitHub releases.

Editorial conclusion

capsule-render suits a developer who wants a decorated README or profile banner without designing an image, and it suits anyone who wants to host their own instance of a small image service. It is the wrong choice if you need a guarantee, because the project states the hosted service is provided on a best effort basis and may be unstable under usage limits or traffic spikes, and a poor match if you want a template library of finished designs rather than a parameterised renderer. Before relying on it, fork the repository and deploy it to your own Vercel instance, and check the theme and palette files in the source if you want the colours to match a specific look rather than one of the built in combinations.

Frequently asked questions

How do I use capsule-render to make a banner?

Add query parameters to the api endpoint of the service and embed the result as an image. The simplest example is a wave type with an automatic colour, a height, the header section and some text, and the same URL works in Markdown and in an HTML img tag.

Which shapes and colours does capsule-render support?

The type parameter offers wave, egg, shark, slice, rect, soft, rounded, cylinder, waving, venom, speech, blur, pulse, checkered and transparent. The colour parameter offers auto and timeAuto for proven random colours, gradient and timeGradient for gradients, a hex code for a solid colour, and a custom gradient written as percent to colour pairs.

Is the hosted capsule-render service reliable enough to depend on?

The project states the service is provided on a best effort basis and may be unstable due to usage limits or traffic spikes, and recommends forking the repository and deploying to your own Vercel instance or another hosting platform for reliable use.

What licence is capsule-render released under?

The MIT licence, with the LICENSE file in the repository root. The project also ships translated readmes in eleven languages under the docs directory, and uses Jest snapshot tests to check the rendered images.

Official sources

  1. Issues
  2. kyechan99/capsule-render on GitHub
  3. License: MIT
  4. Project website
  5. README
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/kyechan99-capsule-render.svg)](https://hysenlabs.com/projects/kyechan99-capsule-render)