A complete Roblox announcement / "admin abuse" broadcast system in a single Luau script.
Owners and admins type :announce Server restart in 5 minutes in chat and a glowing banner
slams onto the top of every player's screen β with sounds, camera shake, confetti, countdowns,
tickers, alerts, a full Owner Console GUI, and a chaos mode that pretends the server is losing
its mind.
12,300+ lines Β· 39 commands Β· 6 ranks Β· 8 visual styles Β· 0 dependencies Β· MIT licensed
- What you get
- Two minute install
- Your first announcement
- Commands
- Who is allowed to do what
- The Owner Console
- Chaos mode (the "admin abuse" bit)
- Configuration
- Using it from your own scripts
- How one file works as both a Script and a LocalScript
- Troubleshooting
- Project layout
- Testing
- License
Seven announcement surfaces, all rendered at the top of the screen or wherever they read best:
| Surface | What it looks like | Command |
|---|---|---|
| Banner | Glowing card pinned under the topbar with a rank badge, sender, progress bar | :announce |
| Alert | Full-screen takeover: dimmed backdrop, pulsing glow, big title | :alert |
| Toast | Small stacked notifications in the bottom-right corner | :toast |
| Ticker | Scrolling news bar across the very top of the screen | :ticker |
| Countdown | Huge centred numbers that pop on every tick, with a finish word | :countdown |
| MOTD | Message of the day card shown to everyone a few seconds after they join | :motd |
| Effects | Screen flash, camera shake, confetti rain, vignette, blur pulse | :flash :shake :confetti :vignette :blur |
Plus the plumbing that makes it usable in a real game:
- ποΈ Rank system β Owner / Admin / Mod / Helper / VIP / Player, resolved from the place owner,
UserIds, usernames, group ranks, Roblox Teams, game passes, or a runtime
AP_Rankattribute. - π₯οΈ Owner Console β a draggable, resizable GUI with tabs for composing, presets, history, tools and the chaos simulator. Works on PC, mobile and console (floating button + keybinds).
- π‘οΈ Safe by default β every broadcast goes through
TextService:FilterStringAsync, rich-text injection is stripped, messages are length-capped, and every command is rank-gated, rate-limited and logged. - π Repeating announcements β
:schedule 300 Save your progress!or declarative timers inCONFIG.Schedule, including uptime milestones. - πΎ Optional persistence β MOTD, schedules and stats can survive a server restart through a DataStore (off by default so nothing breaks before you enable API access).
- π§© Solo mode β install only the client half and everything still works locally, console and all, so you can demo it in an empty baseplate.
- ποΈ Scriptable β the file returns a namespace, so other scripts can call
AnnouncementPanel.Announce("Boss fight started!")directly.
- Open your place in Studio.
- Server half (required for real announcements):
- In the Explorer, right-click ServerScriptService β Insert Object β Script.
- Rename it to
AnnouncementPanel. - Paste the entire contents of
AnnouncementPanel.luauinto it.
- Client half (optional, adds the Owner Console, keybinds, camera shake and local sounds):
- Right-click StarterPlayer β StarterPlayerScripts β Insert Object β LocalScript.
- Rename it to
AnnouncementPanel. - Paste the same file contents into it.
- Press Play. You should see the ASCII banner in the Output window and, if you are the place owner (or you are testing in Studio), a welcome announcement at the top of the screen.
The same file goes in both places. It detects whether it is running as a
Scriptor aLocalScriptand boots the correct half. See How one file works as both.
default.project.json maps the one source file into both
ServerScriptService (as a Script) and StarterPlayerScripts (as a LocalScript):
rojo serve
# then connect with the Rojo plugin in StudioPut the file in a ModuleScript (for example ReplicatedStorage.AnnouncementPanel) and require
it from your own code. It boots the half that matches the requiring context and hands you the
namespace back.
β οΈ Use this instead of the Script/LocalScript install, not as well as it. The panel boots when the file runs, so two copies in the same context means two servers fighting over the same ScreenGuis and chat hooks.
In Studio, press Play and type into the chat:
:announce Hello world
A banner slides in under the topbar. Now try:
:alert The server is restarting NOW --title EMERGENCY -t 12
:toast Double XP is live -c gold
:ticker Weekend event all weekend long --tag LIVE
:countdown 10 Boss fight --finish GO!
:confetti
:chaos on 30
:help
:help lists only the commands your rank is allowed to use, with usage and examples.
:whoami tells you which rank you were given and exactly why.
39 commands, all prefixed with : (change CONFIG.CommandPrefix). Full reference with every
alias, flag and example: docs/COMMANDS.md.
Announcements β 11 commands
| Command | Aliases | What it does |
|---|---|---|
:announce <msg> |
:a :say :msg |
Glowing banner at the top of everyone's screen |
:alert <msg> |
:alarm :emergency :urgent |
Full-screen takeover with a dimmed backdrop |
:toast <msg> |
:notify :n :note |
Small notification, bottom-right |
:ticker <msg> |
:news :marquee :scroll |
Scrolling news bar at the very top |
:countdown <s> [label] |
:timer :cd :count |
Big centred countdown |
:motd <msg> |
:welcome |
Message of the day shown on join |
:broadcast <msg> |
:bc |
Golden banner forced to every player |
:whisper <msg> |
:w :staff :sc |
Banner only staff can see |
:test [msg] |
:preview :demo |
Sends an announcement to you only |
:preset <name> |
:quick :p |
Fires a pre-configured bundle |
:clear |
:clean :stop :hide |
Removes everything from the screen |
Effects β 6 commands
| Command | Aliases | What it does |
|---|---|---|
:flash |
:strobe :blink |
Flashes the screen |
:shake |
:quake :earthquake |
Shakes every camera and the panel |
:confetti |
:party :celebrate |
Rains confetti |
:vignette on|off |
:dark :focus |
Darkens the screen edges |
:blur |
:focusblur |
Blur pulse (server install) |
:chaos on|off [s] |
:abuse :adminabuse :party-mode |
The admin-abuse show |
Moderation β 6 commands
| Command | Aliases | What it does |
|---|---|---|
:mute <player> [s] [reason] |
:silence :shush |
Stops a player's chat being delivered |
:unmute <player> |
:unsilence |
Lifts a mute |
:mutes |
:mutelist |
Lists active mutes |
:rank <player> <rank> |
:promote :setrank |
Session rank, lasts until they leave |
:unrank <player> |
:demote |
Removes a session rank |
:kick <player> [reason] |
:remove :boot |
Announces the kick, then kicks (Owner only) |
System β 6 commands
| Command | Aliases | What it does |
|---|---|---|
:schedule <every> <msg> |
:sched :every |
Repeats an announcement on a timer |
:schedules |
:timers |
Lists active timers |
:unschedule <id|all> |
:unsched |
Removes a timer |
:panel |
:console :ui |
Opens the Owner Console |
:refresh |
:reload |
Clears the rank cache and re-resolves everyone |
:clearhistory |
:ch |
Empties the history log |
Information β 10 commands
:help :stats :history :ranks :whoami :styles :colors :presets :version :ping
Flags work anywhere after the command name, in any order:
:announce Save your progress -t 15 -s Gold -c crimson --to admins --title "SERVER RESTART"
| Flag | Short | Meaning |
|---|---|---|
--time <seconds> |
-t |
How long it stays on screen |
--style <name> |
-s |
One of the 8 visual styles |
--color <name> |
-c |
One of 23 colour names, #RRGGBB, or 255,120,60 |
--to <target> |
all admins owners mods staff nonstaff random me |
|
--title <text> |
Title line above the message | |
--tag <text> |
Ticker tag chip (e.g. LIVE) |
|
--finish <text> |
Word shown when a countdown hits zero | |
--kind <name> |
Force Banner / Toast / Alert / Ticker |
Quoting: "wrap multi word values in quotes". Everything after a bare -- is treated as message
text, so :announce -- -t is not a flag here works.
- Styles (8):
DefaultGoldFireIceNeonShadowRainbowEmergency - Colours (23):
redcrimsonorangegoldyellowlimegreenemeraldtealcyanskybluenavypurplevioletpinkmagentawhitesilvergreygrayblackbrown - Presets (10):
restartupdateeventruleswelcomedoublexpmaintenancegiveawaylagshutdown
CONFIG.WhisperPrefix (. by default) turns any announcement into a staff-only one:
.announce meeting in VC reaches staff, never players.
Ranks are resolved top-down by Order β a player keeps the highest rank they match:
| Rank | Order | Badge | Can use | Max duration | Chaos |
|---|---|---|---|---|---|
| Owner | 100 | π | everything (*) |
600s | β |
| Admin | 75 | π‘ | 27 commands incl. moderation | 300s | β |
| Mod | 50 | π¨ | announce, toast, ticker, clear, history, stats, mute, ping, panel | 120s | β |
| Helper | 30 | β¨ | announce, toast, history, help, ping, test | 60s | β |
| VIP | 15 | π | cosmetic tag only | β | β |
| Player | 0 | β | nothing | β | β |
Tell the panel who your staff are in CONFIG.Owners / CONFIG.Admins / CONFIG.Mods /
CONFIG.Helpers / CONFIG.VIPs. Every list is OR'd together and each accepts:
CONFIG.Owners = {
UserIds = { 15645312 }, -- most reliable
Usernames = { "Builderman" }, -- convenient, can break
GroupRanks = { { GroupId = 12345678, MinRank = 250, MaxRank = 255 } },
Teams = { "Administrators" }, -- Roblox Teams
Gamepasses = { [123456789] = true },
}Out of the box these defaults already apply:
GameOwnerIsOwner = trueβ the place owner is always an Owner.StudioEveryoneIsOwner = trueβ in Studio everyone is an Owner, so you can test instantly. Turn this off before shipping if you don't want every player to have the crown.AllowAttributeOverride = trueβplayer:SetAttribute("AP_Rank", "Admin")at runtime wins, which is how your own admin systems can hand out ranks.DenyListUserIdsβ force specific users down toPlayerno matter what.
Ranks are cached for RankRules.CacheForSeconds (20s) so group/game pass lookups never spam.
:whoami shows the resolution source, and CONFIG.Debug.PrintRankResolution = true logs it.
Install the client half and press Right Control (or F1, or tap the floating button in the bottom-right) to open a full GUI panel:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π OWNER Announcement Panel β β β
β COMPOSE Β· PRESETS Β· HISTORY Β· TOOLS Β· CHAOS β
β β
β message ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Weekend event starts in 10 minutes! β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β title βββββββββββββββββββββββββ 128 / 280 β
β type [Banner][Alert][Toast][Ticker][Countdown] β
β style [Gold][Fire][Ice][Neon][Rainbow][Emergency] β
β colour β β β β β β β β β β β
β target [everyone][admins][mods][staff][me][random] β
β effects [flash][shake][confetti][sound] β
β time [β] 9s [+] [ PREVIEW ] [ π’ SEND ] β
β β
β v2.1.0 β’ companion ready β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Compose β message + title, kind, style, colour swatches, target, effect toggles, duration stepper, character counter, preview (sends to you only) and send.
- Presets β one-click bundles from
CONFIG.Presets, plusCONFIG.Console.QuickMessages. - History β searchable log of everything sent, with kind, sender, style and timestamp.
- Tools β MOTD editor,
:clear,:refresh,:ping, rank list, stats. - Chaos β the abuse simulator: duration stepper, on/off toggle, live status, and buttons that fire individual fake announcements.
The window is draggable (title bar), resizable (bottom-right grip), minimisable, closes on Escape, remembers where you put it, and blurs the game behind it. It recolours itself to your rank β Helpers get green, Admins red, Owners gold.
Companion mode (both halves installed): the console sends your command to the server over a RemoteEvent, the server validates rank/cooldowns/filters exactly like chat, and pushes a state packet back so the console history and stats stay live. Solo mode (client half only): the console runs everything locally so you can still show off.
:chaos on 60
For the next 60 seconds the panel pretends the server has been taken over: fake owners with names
from CONFIG.Chaos.FakeOwnerNames spam dramatic lines from CONFIG.Chaos.Lines, in random styles
and colours, with random flashes, camera shakes, confetti bursts and the occasional full-screen
alert. It auto-stops after CONFIG.Chaos.MaxDurationSeconds (180s) so nobody leaves it running
forever, it is rank-gated (CanUseChaos), and it has its own 30s cooldown.
Everything chaos produces goes through the same broadcaster as real commands β so it is filtered,
history-logged and clearly marked as chaos in :history. :chaos off stops it immediately.
Set CONFIG.Chaos.Enabled = false to remove it entirely (the command disappears from :help).
Everything lives in one CONFIG table near the top of the file β no hunting through 12,000 lines.
Full reference: docs/CONFIGURATION.md.
The five things people change first:
CONFIG.CommandPrefix = ":" -- or ">" or "!" if ":" clashes with your game
CONFIG.RankRules.StudioEveryoneIsOwner = false -- β οΈ before shipping!
CONFIG.Owners.UserIds = { 15645312 } -- you
CONFIG.MOTD.Message = "Read the rules. Be excellent to each other."
CONFIG.Sounds.Banner = "rbxassetid://0000000000" -- your own sound idsOther sections: ServerInfo, Ranks, Limits, Appearance, Animation, Effects, Sounds,
Styles, Presets, MOTD, JoinMessages, LeaveMessages, Schedule, Chaos, Console,
Solo, Moderation, Persistence, Net, Debug.
Rate limits are on by default and are the usual reason a command "did nothing":
| Limit | Default |
|---|---|
CommandCooldown |
1.2s between any two commands, per player |
AnnounceCooldown |
2.5s between announcements, per player |
AlertCooldown |
20s β full-screen alerts are rare on purpose |
ChaosCooldown |
30s |
MaxMessageLength |
280 characters (longer text is truncated) |
MaxVisibleBanners |
3 β older ones collapse out of the way |
The file returns its namespace, so it doubles as a ModuleScript:
-- ServerScriptService/MyGameLogic.server.lua
local AP = require(game.ServerScriptService.AnnouncementPanel)
AP.Announce("Boss fight started!", { Style = "Fire", Duration = 12, Sound = "Alert" })
AP.Alert("Server restart in 60 seconds", { Title = "MAINTENANCE" })
AP.Toast("Double XP is live", { Color = "gold" })
AP.Ticker("Weekend event all weekend long", { Tag = "LIVE" })
AP.Countdown(10, "Boss spawn", { Finish = "GO!" })
AP.StartChaos(nil, 30) -- nil = fired by the system
print(AP.GetRank(player)) -- "Owner"
AP.SetRank(player, "Mod") -- session rank
AP.Clear() -- wipe every screen
local state = AP.GetState() -- stats, history, MOTD, chaos, modeSub-namespaces are exported too, if you want the internals:
AP.Util, AP.Theme, AP.Audio, AP.RankSystem, AP.MessageFilter, AP.ChatBridge, AP.Net,
AP.State, AP.History, AP.Persistence, AP.Scheduler, AP.UIBuilder, AP.Effects,
AP.Renderer, AP.Broadcaster, AP.Commands, AP.CommandParser, AP.CommandService,
AP.Chaos, AP.ServerCore, AP.ClientConsole, AP.Client, AP.Bootstrap, AP.CONFIG.
Custom commands register through the same registry the built-ins use:
AP.Commands.Register({
Name = "boss",
Aliases = { "spawnboss" },
Category = "Announcements",
Usage = ":boss <name>",
Description = "Announce a boss spawn.",
Run = function(ctx)
AP.Announce(ctx.Message .. " has spawned!", { Style = "Fire", Rank = ctx.Rank })
ctx.Reply("Boss announced.", "success")
return true
end,
})local IS_SERVER = RunService:IsServer()
local IS_CLIENT = RunService:IsClient()Those two lines decide everything. The file is written so that each half only touches APIs that exist in its own context:
- Server half (
ScriptinServerScriptService) creates the RemoteEvents, hooks chat (TextChannel.OnIncomingMessageandPlayer.Chatted, deduplicated so nothing runs twice), filters text, resolves ranks, injects a ScreenGui into everyPlayerGui, drives the scheduler, chaos mode, persistence and moderation. - Client half (
LocalScriptinStarterPlayerScripts) builds the Owner Console, binds keybinds, does camera shake and local sounds, and either enhances the server-built GUI (companion) or builds its own display (solo). - When both are present they handshake over
ReplicatedStorage.AnnouncementPanelRemotesand aAP_ServerActiveattribute, so nothing is ever rendered twice and the client never guesses its own rank.
Nothing is required, nothing is downloaded, nothing is parented into ReplicatedStorage except
the four RemoteEvents the two halves talk through.
| Symptom | Fix |
|---|---|
Nothing happens when I type :announce |
You are not ranked. Type :whoami β it tells you your rank and why. In Studio, RankRules.StudioEveryoneIsOwner should make you Owner; add your UserId to CONFIG.Owners.UserIds in a live game. |
| "Slow down - commands are rate limited" | Working as intended. Raise CONFIG.Limits.CommandCooldown / AnnounceCooldown if you want faster spam. |
| "Alert failed: cooldown" | AlertCooldown is 20s by design. |
| The banner appears twice | You installed the file twice in the same context (two Scripts, or two LocalScripts). One of each, maximum. |
| No console GUI | The client half is a LocalScript in StarterPlayerScripts. Check CONFIG.Console.Enabled. Press Right Control / F1, or tap the floating button. |
| Console says SOLO | The server half is missing or booted later. That is fine β solo mode is fully functional, but only you see the announcements. |
| Chat shows my command text | The TextChannel hook suppresses commands on the new chat system. On legacy chat the text can still show; CONFIG.Moderation.WarnInChat controls the replies. |
| No sounds | CONFIG.Sounds.* are empty strings by default (Roblox removed free asset ids). Put your own rbxassetid:// ids in. |
| Output is too noisy / too quiet | CONFIG.Debug.Enabled, PrintCommands, PrintRender, PrintNetwork, PrintRankResolution. |
| DataStore warnings | CONFIG.Persistence.Enabled is false by default. Enable it only after turning on Enable Studio Access to API Services. |
.
βββ AnnouncementPanel.luau β the whole system (12,300+ lines, no dependencies)
βββ README.md β you are here
βββ CHANGELOG.md
βββ LICENSE β MIT
βββ default.project.json β Rojo: one file β Script + LocalScript
βββ docs/
β βββ COMMANDS.md β every command, alias, flag and example
β βββ CONFIGURATION.md β every CONFIG key explained
βββ tests/
βββ README.md β how the headless harness works
βββ roblox_mock.luau β a mock Roblox API (instances, signals, services, tweens)
βββ run_tests.sh β builds and runs all four scenarios
βββ driver_server.luau β 84 checks: server half
βββ driver_client.luau β 121 checks: client half in solo mode
βββ driver_dual.luau β 65 checks: both halves together
βββ driver_legacy.luau β 10 checks: legacy chat, no TextChatService
βββ setup_*.luau β per-scenario world setup
This is a single 12,000-line file with no way to unit test it inside Studio, so it ships with a
headless harness: a mock Roblox API (tests/roblox_mock.luau) that implements the Instance tree,
signals, services, datatypes, a virtual clock and a coroutine scheduler. The panel source is loaded
into that world β twice in the "dual" scenario, once as a server and once as a client sharing the
same DataModel β and driven by real chat lines, button clicks and remote fires.
cd tests && ./run_tests.sh # needs the Luau CLI (luau) on your PATHCurrent status: 280 checks passing across 4 scenarios, zero runtime errors.
| Scenario | Checks | Covers |
|---|---|---|
server |
84 | boot, GUI injection, every command family, ranks, permissions, mutes, chaos, scheduling, persistence round-trip, player lifecycle, kick, shutdown |
client (solo) |
121 | console build, every button/tab/selector, send & preview paths, history search, log, drag input, demo loop, respawn resilience |
dual (companion) |
65 | handshake, state sync, one display root (no double render), serverβclient actions, consoleβserver execution, exploit rejection, malformed payloads |
legacy |
10 | chat still works with no TextChatService at all |
The harness has already earned its keep: it caught four real bugs that static analysis could not β
unwired console tab buttons, a Net.Handshake name collision between a RemoteEvent and a function,
implicit child-name indexing in Renderer.FindServerRoot, and a client/server remote argument
mismatch that silently dropped every console command in companion mode. All four are fixed; see
CHANGELOG.md and tests/README.md.
Static checks also run clean: the file parses with luau-compile and produces zero warnings
from luau-analyze, and every Enum.*, Instance.new, service method and property table in the
file has been cross-checked against Roblox's published API definitions.
MIT β see LICENSE. Use it, fork it, ship it, put your name on it. Attribution is appreciated but not required.