GlowAPI
Makes players and entities glow in any of the sixteen chat colors, and does it per receiving player.
The vanilla glowing effect is one flag on the entity, so it looks the same to everyone who can see it. That's fine until you want it to mean something. A team plugin wants your own team outlined green and the enemy red, at the same time, on the same entity. A staff tool wants a vanished player outlined only for other staff. A minigame wants the last player alive highlighted for the spectators and nobody else.
None of that is possible with one server-side flag. GlowAPI does it client-side instead: the glow bit and the scoreboard team that carries the color are sent to each viewer individually, so player A can see player B glowing red while player C sees that same player B glowing blue, and player B sees nothing at all.
This is a maintained continuation of InventivetalentDev's GlowAPI, which stopped being updated years ago. Same package, same class, same method signatures — plugins written against the old one keep working without a single code change.
What changed
The old version needed PacketListenerAPI installed alongside it, and that dependency died first. When it stopped being updated, everything built on GlowAPI went with it.
The packet layer is now PacketEvents, bundled inside the jar and relocated, so nothing else has to be installed and nothing collides with another plugin's copy. That also carried the supported range from roughly 1.19 up to the current release.
| old GlowAPI | GlowAPI 2.0 | |
|---|---|---|
| Packet layer | PacketListenerAPI (dead) | PacketEvents, bundled |
| Extra plugins to install | one | none |
| Minecraft versions | up to ~1.19 | 1.9 through 26.2 |
| Public API | — | unchanged |
Minecraft 1.8.x is not supported and won't be. The glowing effect didn't exist before 1.9, so there is nothing to send.
Requirements
- Bukkit, Spigot, Paper or a fork, on Minecraft 1.9 or newer
- Nothing else. PacketEvents is inside the jar
If you already run PacketEvents as its own plugin, GlowAPI notices and uses that instance instead of starting a second one.
Drop the jar in plugins/ and restart. There is no setup step — the plugin is idle until another plugin asks it for something, or until you type /glow.
Using it from your plugin
Add GlowAPI as a soft dependency in your plugin.yml:
softdepend: [GlowAPI]
Then call it. Every method is static, so there is no instance to fetch and no service to look up:
// green for one viewer
GlowAPI.setGlowing(target, GlowAPI.Color.GREEN, viewer);
// red for everyone online
GlowAPI.setGlowing(target, GlowAPI.Color.RED, Bukkit.getOnlinePlayers());
// stop glowing for that viewer
GlowAPI.setGlowing(target, (GlowAPI.Color) null, viewer);
The one thing to watch is timing on join. A player isn't ready to receive entity packets in the same tick they fire PlayerJoinEvent, so give it a moment:
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Bukkit.getScheduler().runTaskLater(this, () ->
GlowAPI.setGlowing(event.getPlayer(), GlowAPI.Color.DARK_AQUA, Bukkit.getOnlinePlayers()), 10L);
}
Getting the API into your build
The artifact lives in Codeberg's package registry.
Maven
<repositories>
<repository>
<id>codeberg-glowapi</id>
<url>https://codeberg.org/api/packages/LucasTHCR/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.inventivetalent</groupId>
<artifactId>glowapi</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
Gradle (Kotlin DSL)
repositories {
maven("https://codeberg.org/api/packages/LucasTHCR/maven")
}
dependencies {
compileOnly("org.inventivetalent:glowapi:2.0.1")
}
Gradle (Groovy DSL)
repositories {
maven { url 'https://codeberg.org/api/packages/LucasTHCR/maven' }
}
dependencies {
compileOnly 'org.inventivetalent:glowapi:2.0.1'
}
provided and compileOnly are not a detail you can skip. GlowAPI is GPLv3, so shading it
into your own jar would put your whole plugin under GPLv3 as well. Compiling against it and
letting the server supply the jar at runtime does not - your plugin keeps whatever licence
you chose. It also means your users install one GlowAPI, not one copy per plugin.
Server owners install the jar from the download page; nothing extra goes into your build.
The full surface
| Method | Does |
|---|---|
setGlowing(Entity, Color, Player) |
Sets the color of one entity for one viewer |
setGlowing(Entity, Color, Collection<Player>) |
Same, for several viewers |
setGlowing(Collection<Entity>, Color, Player) |
Several entities, one viewer |
setGlowing(Collection<Entity>, Color, Collection<Player>) |
Several entities, several viewers |
setGlowing(Entity, boolean, Player) |
Glow on or off in the default white |
setGlowing(Entity, Color, String tagVisibility, String push, Player) |
As above, overriding the team's name-tag and collision rules for this call |
isGlowing(Entity, Player) |
Whether that viewer currently sees it glowing |
isGlowing(Entity, Collection<Player>, boolean checkAll) |
checkAll decides between all viewers and any viewer |
getGlowColor(Entity, Player) |
The Color that viewer sees, or null |
initTeam(Player) |
Creates the glow teams for a viewer. Runs on join by itself; you only need it if you build your own login flow |
Color is the sixteen chat colors — BLACK, DARK_BLUE, DARK_GREEN, DARK_AQUA, DARK_RED, DARK_PURPLE, GOLD, GRAY, DARK_GRAY, BLUE, GREEN, AQUA, RED, PURPLE, YELLOW, WHITE — plus NONE.
NONE means glowing without joining a color team, which the client draws in white. Passing null instead of a Color is the one that turns the glow off.
Anything with a UUID works, not just players: mobs, armor stands, dropped items, item frames.
Commands
The commands exist so you can check the plugin works without writing code first. The real interface is the API.
| Command | Permission | Does |
|---|---|---|
/glow <color|off> [player] |
glowapi.command.glow |
Makes a player glow for everyone online. Without a name, yourself |
/glowapi reload |
glowapi.command.admin |
Re-reads config.yml and rebuilds the teams |
/glowapi version |
glowapi.command.admin |
Prints the running version |
/glowapi debug |
glowapi.command.admin |
Shows the detected client version and the colour ids being sent |
Both permissions default to OP. Tab completion covers the colors and the online players.
Configuration
Four values, and the defaults are what you want unless you have a reason otherwise.
# always | never | hideForOtherTeams | hideForOwnTeam
nameTagVisibility: always
# always | never | pushOtherTeams | pushOwnTeam
collision: always
# auto | always | never
modern-team-colors: auto
bstats: true
modern-team-colors exists because Minecraft encodes colours as a bit field — blue, green, red, bright — and clients from 26.1 on read the red and blue bits the other way round. Left alone, aqua reaches those clients as yellow and dark_blue as dark_red, while green, gray, black and white look fine because their red and blue bits are the same. auto swaps the two bits for clients on 26.1 and newer and leaves older ones untouched, so it is correct whatever your players connect with. It follows each viewer's client, not the server, so a mixed-version server behind a protocol translator is handled too. /glowapi debug shows you what is being sent.
bstats: true sends anonymous server counts to bStats. Set it to false and nothing is sent.
The color is carried by a scoreboard team, and a team also decides whether its members' name tags are visible and whether they push each other. Those two settings are what a glowing player inherits along with the color, which is why they're configurable here rather than being fixed.
Set collision: never and glowing players walk through each other, which is usually a surprise rather than a feature. Leave both on always and glow is purely cosmetic.
A plugin that needs different rules for one particular call can pass them to setGlowing directly instead of changing the file.
A note on other scoreboard plugins
The teams are named GAPI#<COLOR> and are sent to the client only. They never touch the server-side scoreboard, so a tab-list or nametag plugin managing its own teams won't be overwritten.
The overlap that does exist is the client's, not the server's: a player can only be in one team at a time on the client. If another plugin puts a player in a client-side team for a prefix, whichever of the two sent its packet last is the one the client keeps. This is a limitation of the vanilla protocol, not something either plugin can work around.
Support
Maintained by LucasTHCR. Original work by InventivetalentDev.
Bug reports and questions go to dc.gg/paperstream. Include your server version and the plugin doing the calls — glow bugs are almost always a conflict with something else touching teams or entity metadata, and knowing what else is installed saves a round of guessing.
License
Copyright (C) 2026 LucasTHCR
GlowAPI is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
The original GlowAPI was released under the MIT License by Haylee Schäfer, and those terms still govern that original code. The jar as a whole is GPLv3 because the bundled PacketEvents is GPL-3.0, and a work containing GPL-3.0 code has to be passed on under GPL-3.0 in full. MIT is GPL-compatible, which is what makes that possible.
Short version: use it, study it, change it, pass it on. Anything you distribute that is built on it has to be GPLv3 too and has to ship its source.
This does not reach into your plugin. Calling GlowAPI at runtime, with it installed as its own jar next to yours, is not distributing a combined work. Your plugin stays under whatever license you picked for it. What GPLv3 asks is that you don't shade GlowAPI into your own jar and then ship that under different terms.
Pinned Versions
- R1.9–26.2
Pages
Members
1Owner