§ How to · with Picoo

How to make a Roblox emote system (with AI)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A Roblox emote system needs four pieces: a radial wheel UI, animation triggering, owned emote tracking, and unlock progression. Describe the emotes you want and Picoo writes the scripts for them. The build below is one example of that output.

Example build: 4 files · 130 lines. Picoo writes this code for each prompt, so file names and line counts vary from run to run. This one has an 8-slot wheel.

Radial wheel UI

Hold B to open the wheel, point the mouse at an emote, and release to play it. 8 slots in this example; the slot count is a setting.

Animation triggering

The server checks that the player owns the emote, then loads it on the character's Animator and plays it. Animations played on a character replicate, so every player sees it.

Owned emote tracking

Ownership is saved per player in a DataStore. Default emotes are free for everyone; premium ones unlock through whatever your game uses, such as a gamepass, a quest reward or a shop purchase.

Unlock progression

A new unlock shows a pop-up. Optional rarity tiers (common, rare, epic) color the glow around each emote icon.

Cooldown between emotes

After an emote, the player waits that emote's cooldown (2 seconds by default) before playing another. The wheel darkens while the cooldown runs.

Looping and one-shot emotes

Each emote is looping (a dance) or one-shot (a wave). In this example a looping emote stops when the player moves or uses another action.

Example output from a build like this

4 files in this example. Picoo writes this code for your prompt, so file names and sizes vary.

ServerScriptService/EmoteManager.lua

Ownership validation, animation dispatch

ReplicatedStorage/Modules/EmoteConfig.lua

Per-emote animation IDs, rarity, ownership rules

ReplicatedStorage/Remotes/PlayEmote

Client → server play request

StarterGui/EmoteWheel.lua

Radial wheel UI + selection

Sample output: ServerScriptService/EmoteManager.lua

--!strict
-- ServerScriptService/EmoteManager.lua  (Picoo · excerpt)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Config = require(ReplicatedStorage.Modules.EmoteConfig)
local PlayEmote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("PlayEmote")

local lastPlayed: { [Player]: number } = {}
local owned: { [Player]: { [string]: boolean } } = {}

local function isOwned(player: Player, emoteId: string): boolean
    local emote = Config[emoteId]
    if not emote then return false end
    if emote.default then return true end
    return owned[player] and owned[player][emoteId] or false
end

PlayEmote.OnServerEvent:Connect(function(player: Player, emoteId: string)
    local now = os.clock()
    if (lastPlayed[player] or 0) > now then return end  -- on cooldown
    if not isOwned(player, emoteId) then return end

    local emote = Config[emoteId]
    local character = player.Character
    if not character then return end
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if not humanoid then return end

    local animator = humanoid:FindFirstChildOfClass("Animator")
    if not animator then return end

    local track = animator:LoadAnimation(emote.animation)
    track.Looped = emote.looped or false
    track:Play()

    lastPlayed[player] = now + (emote.cooldown or 2)
end)

Building a Roblox emote system

Emote systems are the social glue of social Roblox games. Players use emotes to greet friends, celebrate wins, and just have fun. A clean emote wheel makes the game feel modern; a chat-command-only system feels dated. Here is one example of what Picoo writes for it.

In this example Picoo wrote four files. EmoteConfig.lua holds per-emote definitions: animation, rarity, ownership rule, cooldown. EmoteManager.lua handles server-side validation and plays the animation. EmoteWheel.lua is the client UI that opens when B is held and selects emotes by mouse direction.

The radial wheel is the canonical pattern. Hold a key (B here), the wheel fades in at the center of the screen, mouse direction highlights a slot, and releasing the key plays the highlighted emote. Eight slots give variety without clutter; games built around emotes can raise the slot count.

Server-side ownership validation is the abuse prevention layer. The client sends a 'play emote X' request, and the server checks that the player owns it before playing the animation, so forged requests for premium emotes are ignored. Ownership is cached per session for fast repeat plays.

Cooldowns prevent emote spam. After an emote, the player waits 2 seconds by default before the next one: long enough to prevent visual chaos, short enough that normal social use feels responsive. The wheel darkens while the cooldown runs, so players know when their next emote is available.

Looping emotes such as dances stop when the player moves or uses another action, so a dance never gets in the way of play. Sounds, props and an emote shop are follow-up prompts on top of the same config.

See more on the Luau generator, the AI Luau script writer, the 3D model generator and the game builder, read how Picoo compares with ChatGPT, or browse the full blog.

Frequently asked

How do I add a new emote?+

Add an entry to EmoteConfig.lua: id, displayName, animation (Animation instance), looped (bool), cooldown (seconds), rarity, default (bool — true for free-to-all). EmoteManager picks it up automatically.

Can emotes be locked behind gamepasses?+

Yes. Give the emote a gamepassId in EmoteConfig and have isOwned check it with MarketplaceService:UserOwnsGamePassAsync, caching the answer for the session. Ask for it in your prompt, with the pass ID.

How do I unlock an emote from a quest reward?+

Ask for a grant step, for example a BindableEvent that marks the emote as owned and saves it to the DataStore, and call it from your quest's completion code. EmoteManager is a server Script, so other scripts reach it through an event or a shared ModuleScript. Name your quest script in the follow-up prompt so the call goes in the right place.

What stops emote spam from being annoying?+

After any emote, the player waits that emote's cooldown before the next one: 2 seconds unless the emote sets its own. Give visually loud emotes a longer cooldown. The wheel shows the remaining time, so players know when they can play again.

Can I have emotes with sound?+

Yes. Ask for a soundId field in EmoteConfig and have EmoteManager play that sound from the character when the emote starts. It is a small follow-up prompt.

Related Picoo prompts