UIAnimController - lightweight client UI animations + frame toggles (hover / click effects, slide-in frames)

UIAnimController is a small, free, and easy-to-use client-side UI utility that automatically adds nice hover and click animations to TextButton / ImageButton and simple debounced slide-in-frame toggles. Everything is scale-based and AnchorPoint-safe.

UIAnimController

Features:

  • Automatically finds and watches buttons in the player’s PlayerGui (including cloned ones).
  • Hover effect (slight scale up) and click effect (shrink → bounce → return) with debounce and sound hooks.
  • Frame toggling by button attribute: opens a target frame with a slide-up animation and closes any other open frame first.
  • Compatible with any AnchorPoint.
  • Ignores buttons whose Size uses Offset (works only with scale-based sizes).
  • Simple API: .init(), .open_by_name(), .close_by_name()
  • and finally, easy customization via script.Config.

PREVIEW:


(sry for quality, using roblox default recorder, my obs settings got messed up)


Quick install:

  1. Import Free Model: FROM HERE
  2. Place the UIAnimController module in ReplicatedStorage
  3. Create a LocalScript in StarterGui OR StarterPlayerScripts
  4. Paste in these lines:
local ui = require(game.ReplicatedStorage.UIAnimController)
ui.init()
  1. the installing is done, but it won’t work yet, Read the USAGE bellow

How to connect buttons ⇄ frames

  1. Give the target frame a unique Name (for example menu_frame).

  2. On the button that should toggle that frame, add an Attribute ui_toggle with value "menu_frame" (string).


    ( Alternative attribute name supported: toggle_frame.)

  3. To make a button inside a frame close that frame, either:

  • Add an Attribute close = true to that close button; or
  • Add a BoolValue child named close with Value set to true to the close button.*
  1. Notes:
  • Frames slide in from the bottom (off-screen) to the frame’s stored default Position. Size is never changed.
  • Buttons must use scale-only UDim2 sizes (no Offset); otherwise the button will be ignored.

at this point, you’re already done setting it up. but you can keep reading c:

API (main module):

  • module.init(player?)
    Start the system. If player omitted it uses Players.LocalPlayer.

  • module.open_by_name(name)
    Finds a frame in the player GUI by Name (recursive search) and opens it.

  • module.close_by_name(name)
    Finds a frame by Name and closes it.

(Internals are split across Manager/* - Frame and Button hold the logic. No changes needed for normal usage.)

Config / customization:

Open script/Config to change:

  • hover / click scales (hover_scale, click_shrink, click_bounce)
  • sound IDs (hover_sound_id, click_sound_id) → set to "rbxassetid://<id>" strings
  • tween durations and easing (tween_info_*, frame_open_time, frame_close_time)

You can change these values at edit-time or add a small wrapper to allow runtime configuration.

Limitations & Tips:

  • Only supports buttons with scale-only Size (no Offset). This was intentional to keep the position compensation simple and robust.
  • If you use AutomaticSize or UI Layouts, the script waits a couple of Heartbeats when the frame opens for the layout to settle — this avoids the first-open flash. If you still see a flash for a specific complex frame, mention its layout (AutomaticSize/UIListLayout) and I can provide a tweak.
  • Everything is client-side (LocalScript) — do not use for server-side UI logic.
  • AnchorPoint: fully supported (script compensates for AnchorPoint so visual center doesn’t jump).

Thats all, I know it’s not the best but i hope this is helpful for some people, Enjoy!

1 Like

most creative resource name ive seen in years

1 Like

funny part is that i can’t even tell if this is sarcastic or not lol

why all code in snake_case, use camelCase or PascalCase for function declaration and, global variables, also make readable code. This code may be clear to you, but it often confuses other people.

just a consistency choice. I use snake_case to clearly separate my own variables/functions from Roblox’s PascalCase classes and services, which makes the code faster to scan for me

1 Like